/* RTL simplification functions for GNU compiler.
- Copyright (C) 1987-2015 Free Software Foundation, Inc.
+ Copyright (C) 1987-2017 Free Software Foundation, Inc.
This file is part of GCC.
#include "rtl.h"
#include "tree.h"
#include "predict.h"
+#include "memmodel.h"
#include "optabs.h"
#include "emit-rtl.h"
#include "recog.h"
#include "diagnostic-core.h"
#include "varasm.h"
#include "flags.h"
+#include "selftest.h"
+#include "selftest-rtl.h"
/* Simplification and canonicalization of RTL. */
occasionally need to sign extend from low to high as if low were a
signed wide int. */
#define HWI_SIGN_EXTEND(low) \
- ((((HOST_WIDE_INT) low) < 0) ? ((HOST_WIDE_INT) -1) : ((HOST_WIDE_INT) 0))
+ ((((HOST_WIDE_INT) low) < 0) ? HOST_WIDE_INT_M1 : HOST_WIDE_INT_0)
static rtx neg_const_int (machine_mode, const_rtx);
static bool plus_minus_operand_p (const_rtx);
static rtx simplify_plus_minus (enum rtx_code, machine_mode, rtx, rtx);
-static rtx simplify_immed_subreg (machine_mode, rtx, machine_mode,
- unsigned int);
static rtx simplify_associative_operation (enum rtx_code, machine_mode,
rtx, rtx);
static rtx simplify_relational_operation_1 (enum rtx_code, machine_mode,
static rtx simplify_binary_operation_1 (enum rtx_code, machine_mode,
rtx, rtx, rtx, rtx);
\f
-/* Negate a CONST_INT rtx, truncating (because a conversion from a
- maximally negative number can overflow). */
+/* Negate a CONST_INT rtx. */
static rtx
neg_const_int (machine_mode mode, const_rtx i)
{
- return gen_int_mode (-(unsigned HOST_WIDE_INT) INTVAL (i), mode);
+ unsigned HOST_WIDE_INT val = -UINTVAL (i);
+
+ if (!HWI_COMPUTABLE_MODE_P (mode)
+ && val == UINTVAL (i))
+ return simplify_const_unary_operation (NEG, mode, CONST_CAST_RTX (i),
+ mode);
+ return gen_int_mode (val, mode);
}
/* Test whether expression, X, is an immediate constant that represents
{
unsigned HOST_WIDE_INT val;
unsigned int width;
+ scalar_int_mode int_mode;
- if (GET_MODE_CLASS (mode) != MODE_INT)
+ if (!is_int_mode (mode, &int_mode))
return false;
- width = GET_MODE_PRECISION (mode);
+ width = GET_MODE_PRECISION (int_mode);
if (width == 0)
return false;
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));
+ val &= (HOST_WIDE_INT_1U << width) - 1;
+ return val == (HOST_WIDE_INT_1U << (width - 1));
}
/* Test whether VAL is equal to the most significant bit of mode MODE
val_signbit_p (machine_mode mode, unsigned HOST_WIDE_INT val)
{
unsigned int width;
+ scalar_int_mode int_mode;
- if (GET_MODE_CLASS (mode) != MODE_INT)
+ if (!is_int_mode (mode, &int_mode))
return false;
- width = GET_MODE_PRECISION (mode);
+ width = GET_MODE_PRECISION (int_mode);
if (width == 0 || width > HOST_BITS_PER_WIDE_INT)
return false;
- val &= GET_MODE_MASK (mode);
- return val == ((unsigned HOST_WIDE_INT) 1 << (width - 1));
+ val &= GET_MODE_MASK (int_mode);
+ return val == (HOST_WIDE_INT_1U << (width - 1));
}
/* Test whether the most significant bit of mode MODE is set in VAL.
{
unsigned int width;
- if (GET_MODE_CLASS (mode) != MODE_INT)
+ scalar_int_mode int_mode;
+ if (!is_int_mode (mode, &int_mode))
return false;
- width = GET_MODE_PRECISION (mode);
+ width = GET_MODE_PRECISION (int_mode);
if (width == 0 || width > HOST_BITS_PER_WIDE_INT)
return false;
- val &= (unsigned HOST_WIDE_INT) 1 << (width - 1);
+ val &= HOST_WIDE_INT_1U << (width - 1);
return val != 0;
}
{
unsigned int width;
- if (GET_MODE_CLASS (mode) != MODE_INT)
+ scalar_int_mode int_mode;
+ if (!is_int_mode (mode, &int_mode))
return false;
- width = GET_MODE_PRECISION (mode);
+ width = GET_MODE_PRECISION (int_mode);
if (width == 0 || width > HOST_BITS_PER_WIDE_INT)
return false;
- val &= (unsigned HOST_WIDE_INT) 1 << (width - 1);
+ val &= HOST_WIDE_INT_1U << (width - 1);
return val == 0;
}
\f
/* If we're accessing the constant in a different mode than it was
originally stored, attempt to fix that up via subreg simplifications.
If that fails we have no choice but to return the original memory. */
- if ((offset != 0 || cmode != GET_MODE (x))
- && offset >= 0 && offset < GET_MODE_SIZE (cmode))
+ if (offset == 0 && cmode == GET_MODE (x))
+ return c;
+ else if (offset >= 0 && offset < GET_MODE_SIZE (cmode))
{
rtx tem = simplify_subreg (GET_MODE (x), c, cmode, offset);
if (tem && CONSTANT_P (tem))
return tem;
}
- else
- return c;
}
return x;
{
tree decl = MEM_EXPR (x);
machine_mode mode = GET_MODE (x);
- HOST_WIDE_INT offset = 0;
+ poly_int64 offset = 0;
switch (TREE_CODE (decl))
{
case IMAGPART_EXPR:
case VIEW_CONVERT_EXPR:
{
- HOST_WIDE_INT bitsize, bitpos;
+ poly_int64 bitsize, bitpos, bytepos, toffset_val = 0;
tree toffset;
int unsignedp, reversep, volatilep = 0;
decl
= get_inner_reference (decl, &bitsize, &bitpos, &toffset, &mode,
- &unsignedp, &reversep, &volatilep, false);
- if (bitsize != GET_MODE_BITSIZE (mode)
- || (bitpos % BITS_PER_UNIT)
- || (toffset && !tree_fits_shwi_p (toffset)))
+ &unsignedp, &reversep, &volatilep);
+ if (maybe_ne (bitsize, GET_MODE_BITSIZE (mode))
+ || !multiple_p (bitpos, BITS_PER_UNIT, &bytepos)
+ || (toffset && !poly_int_tree_p (toffset, &toffset_val)))
decl = NULL;
else
- {
- offset += bitpos / BITS_PER_UNIT;
- if (toffset)
- offset += tree_to_shwi (toffset);
- }
+ offset += bytepos + toffset_val;
break;
}
}
if (decl
&& mode == GET_MODE (x)
- && TREE_CODE (decl) == VAR_DECL
+ && VAR_P (decl)
&& (TREE_STATIC (decl)
|| DECL_THREAD_LOCAL_P (decl))
&& DECL_RTL_SET_P (decl)
if (MEM_P (newx))
{
rtx n = XEXP (newx, 0), o = XEXP (x, 0);
+ poly_int64 n_offset, o_offset;
/* Avoid creating a new MEM needlessly if we already had
the same address. We do if there's no OFFSET and the
form (plus NEWX OFFSET), or the NEWX is of the form
(plus Y (const_int Z)) and X is that with the offset
added: (plus Y (const_int Z+OFFSET)). */
- if (!((offset == 0
- || (GET_CODE (o) == PLUS
- && GET_CODE (XEXP (o, 1)) == CONST_INT
- && (offset == INTVAL (XEXP (o, 1))
- || (GET_CODE (n) == PLUS
- && GET_CODE (XEXP (n, 1)) == CONST_INT
- && (INTVAL (XEXP (n, 1)) + offset
- == INTVAL (XEXP (o, 1)))
- && (n = XEXP (n, 0))))
- && (o = XEXP (o, 0))))
+ n = strip_offset (n, &n_offset);
+ o = strip_offset (o, &o_offset);
+ if (!(known_eq (o_offset, n_offset + offset)
&& rtx_equal_p (o, n)))
x = adjust_address_nv (newx, mode, offset);
}
else if (GET_MODE (x) == GET_MODE (newx)
- && offset == 0)
+ && known_eq (offset, 0))
x = newx;
}
}
rtx tem;
/* If this simplifies, use it. */
- if (0 != (tem = simplify_ternary_operation (code, mode, op0_mode,
- op0, op1, op2)))
+ if ((tem = simplify_ternary_operation (code, mode, op0_mode,
+ op0, op1, op2)) != 0)
return tem;
return gen_rtx_fmt_eee (code, mode, op0, op1, op2);
{
rtx tem;
- if (0 != (tem = simplify_relational_operation (code, mode, cmp_mode,
- op0, op1)))
+ if ((tem = simplify_relational_operation (code, mode, cmp_mode,
+ op0, op1)) != 0)
return tem;
return gen_rtx_fmt_ee (code, mode, op0, op1);
{
unsigned int precision = GET_MODE_UNIT_PRECISION (mode);
unsigned int op_precision = GET_MODE_UNIT_PRECISION (op_mode);
+ scalar_int_mode int_mode, int_op_mode, subreg_mode;
+
gcc_assert (precision <= op_precision);
/* Optimize truncations of zero and sign extended values. */
return simplify_gen_binary (ASHIFT, mode,
XEXP (XEXP (op, 0), 0), XEXP (op, 1));
+ /* Likewise (truncate:QI (and:SI (lshiftrt:SI (x:SI) C) C2)) into
+ (and:QI (lshiftrt:QI (truncate:QI (x:SI)) C) C2) for suitable C
+ and C2. */
+ if (GET_CODE (op) == AND
+ && (GET_CODE (XEXP (op, 0)) == LSHIFTRT
+ || GET_CODE (XEXP (op, 0)) == ASHIFTRT)
+ && CONST_INT_P (XEXP (XEXP (op, 0), 1))
+ && CONST_INT_P (XEXP (op, 1)))
+ {
+ rtx op0 = (XEXP (XEXP (op, 0), 0));
+ rtx shift_op = XEXP (XEXP (op, 0), 1);
+ rtx mask_op = XEXP (op, 1);
+ unsigned HOST_WIDE_INT shift = UINTVAL (shift_op);
+ unsigned HOST_WIDE_INT mask = UINTVAL (mask_op);
+
+ if (shift < precision
+ /* If doing this transform works for an X with all bits set,
+ it works for any X. */
+ && ((GET_MODE_MASK (mode) >> shift) & mask)
+ == ((GET_MODE_MASK (op_mode) >> shift) & mask)
+ && (op0 = simplify_gen_unary (TRUNCATE, mode, op0, op_mode))
+ && (op0 = simplify_gen_binary (LSHIFTRT, mode, op0, shift_op)))
+ {
+ mask_op = GEN_INT (trunc_int_for_mode (mask, mode));
+ return simplify_gen_binary (AND, mode, op0, mask_op);
+ }
+ }
+
+ /* Turn (truncate:M1 (*_extract:M2 (reg:M2) (len) (pos))) into
+ (*_extract:M1 (truncate:M1 (reg:M2)) (len) (pos')) if possible without
+ changing len. */
+ if ((GET_CODE (op) == ZERO_EXTRACT || GET_CODE (op) == SIGN_EXTRACT)
+ && REG_P (XEXP (op, 0))
+ && GET_MODE (XEXP (op, 0)) == GET_MODE (op)
+ && CONST_INT_P (XEXP (op, 1))
+ && CONST_INT_P (XEXP (op, 2)))
+ {
+ rtx op0 = XEXP (op, 0);
+ unsigned HOST_WIDE_INT len = UINTVAL (XEXP (op, 1));
+ unsigned HOST_WIDE_INT pos = UINTVAL (XEXP (op, 2));
+ if (BITS_BIG_ENDIAN && pos >= op_precision - precision)
+ {
+ op0 = simplify_gen_unary (TRUNCATE, mode, op0, GET_MODE (op0));
+ if (op0)
+ {
+ pos -= op_precision - precision;
+ return simplify_gen_ternary (GET_CODE (op), mode, mode, op0,
+ XEXP (op, 1), GEN_INT (pos));
+ }
+ }
+ else if (!BITS_BIG_ENDIAN && precision >= len + pos)
+ {
+ op0 = simplify_gen_unary (TRUNCATE, mode, op0, GET_MODE (op0));
+ if (op0)
+ return simplify_gen_ternary (GET_CODE (op), mode, mode, op0,
+ XEXP (op, 1), XEXP (op, 2));
+ }
+ }
+
/* Recognize a word extraction from a multi-word subreg. */
if ((GET_CODE (op) == LSHIFTRT
|| GET_CODE (op) == ASHIFTRT)
&& (INTVAL (XEXP (op, 1)) & (precision - 1)) == 0
&& UINTVAL (XEXP (op, 1)) < op_precision)
{
- int byte = subreg_lowpart_offset (mode, op_mode);
+ poly_int64 byte = subreg_lowpart_offset (mode, op_mode);
int shifted_bytes = INTVAL (XEXP (op, 1)) / BITS_PER_UNIT;
return simplify_gen_subreg (mode, XEXP (op, 0), op_mode,
(WORDS_BIG_ENDIAN
if the MEM has a mode-dependent address. */
if ((GET_CODE (op) == LSHIFTRT
|| GET_CODE (op) == ASHIFTRT)
- && SCALAR_INT_MODE_P (op_mode)
+ && is_a <scalar_int_mode> (mode, &int_mode)
+ && is_a <scalar_int_mode> (op_mode, &int_op_mode)
&& MEM_P (XEXP (op, 0))
&& CONST_INT_P (XEXP (op, 1))
- && (INTVAL (XEXP (op, 1)) % GET_MODE_BITSIZE (mode)) == 0
+ && INTVAL (XEXP (op, 1)) % GET_MODE_BITSIZE (int_mode) == 0
&& INTVAL (XEXP (op, 1)) > 0
- && INTVAL (XEXP (op, 1)) < GET_MODE_BITSIZE (op_mode)
+ && INTVAL (XEXP (op, 1)) < GET_MODE_BITSIZE (int_op_mode)
&& ! mode_dependent_address_p (XEXP (XEXP (op, 0), 0),
MEM_ADDR_SPACE (XEXP (op, 0)))
&& ! MEM_VOLATILE_P (XEXP (op, 0))
- && (GET_MODE_SIZE (mode) >= UNITS_PER_WORD
+ && (GET_MODE_SIZE (int_mode) >= UNITS_PER_WORD
|| WORDS_BIG_ENDIAN == BYTES_BIG_ENDIAN))
{
- int byte = subreg_lowpart_offset (mode, op_mode);
+ poly_int64 byte = subreg_lowpart_offset (int_mode, int_op_mode);
int shifted_bytes = INTVAL (XEXP (op, 1)) / BITS_PER_UNIT;
- return adjust_address_nv (XEXP (op, 0), mode,
+ return adjust_address_nv (XEXP (op, 0), int_mode,
(WORDS_BIG_ENDIAN
? byte - shifted_bytes
: byte + shifted_bytes));
/* (truncate:A (subreg:B (truncate:C X) 0)) is
(truncate:A X). */
if (GET_CODE (op) == SUBREG
- && SCALAR_INT_MODE_P (mode)
+ && is_a <scalar_int_mode> (mode, &int_mode)
&& SCALAR_INT_MODE_P (op_mode)
- && SCALAR_INT_MODE_P (GET_MODE (SUBREG_REG (op)))
+ && is_a <scalar_int_mode> (GET_MODE (SUBREG_REG (op)), &subreg_mode)
&& GET_CODE (SUBREG_REG (op)) == TRUNCATE
&& subreg_lowpart_p (op))
{
rtx inner = XEXP (SUBREG_REG (op), 0);
- if (GET_MODE_PRECISION (mode)
- <= GET_MODE_PRECISION (GET_MODE (SUBREG_REG (op))))
- return simplify_gen_unary (TRUNCATE, mode, inner, GET_MODE (inner));
+ if (GET_MODE_PRECISION (int_mode) <= GET_MODE_PRECISION (subreg_mode))
+ return simplify_gen_unary (TRUNCATE, int_mode, inner,
+ GET_MODE (inner));
else
/* If subreg above is paradoxical and C is narrower
than A, return (subreg:A (truncate:C X) 0). */
- return simplify_gen_subreg (mode, SUBREG_REG (op),
- GET_MODE (SUBREG_REG (op)), 0);
+ return simplify_gen_subreg (int_mode, SUBREG_REG (op), subreg_mode, 0);
}
/* (truncate:A (truncate:B X)) is (truncate:A X). */
return simplify_gen_unary (TRUNCATE, mode, XEXP (op, 0),
GET_MODE (XEXP (op, 0)));
+ /* (truncate:A (ior X C)) is (const_int -1) if C is equal to that already,
+ in mode A. */
+ if (GET_CODE (op) == IOR
+ && SCALAR_INT_MODE_P (mode)
+ && SCALAR_INT_MODE_P (op_mode)
+ && CONST_INT_P (XEXP (op, 1))
+ && trunc_int_for_mode (INTVAL (XEXP (op, 1)), mode) == -1)
+ return constm1_rtx;
+
return NULL_RTX;
}
\f
simplify_unary_operation_1 (enum rtx_code code, machine_mode mode, rtx op)
{
enum rtx_code reversed;
- rtx temp;
+ rtx temp, elt, base, step;
+ scalar_int_mode inner, int_mode, op_mode, op0_mode;
switch (code)
{
comparison is all ones. */
if (COMPARISON_P (op)
&& (mode == BImode || STORE_FLAG_VALUE == -1)
- && ((reversed = reversed_comparison_code (op, NULL_RTX)) != UNKNOWN))
+ && ((reversed = reversed_comparison_code (op, NULL)) != UNKNOWN))
return simplify_gen_relational (reversed, mode, VOIDmode,
XEXP (op, 0), XEXP (op, 1));
&& 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)
+ /* Similarly, (not (neg X)) is (plus X -1). Only do this for
+ modes that have CONSTM1_RTX, i.e. MODE_INT, MODE_PARTIAL_INT
+ and MODE_VECTOR_INT. */
+ if (GET_CODE (op) == NEG && CONSTM1_RTX (mode))
return simplify_gen_binary (PLUS, mode, XEXP (op, 0),
CONSTM1_RTX (mode));
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
+ && is_a <scalar_int_mode> (mode, &int_mode)
&& GET_CODE (op) == ASHIFTRT
&& CONST_INT_P (XEXP (op, 1))
- && INTVAL (XEXP (op, 1)) == GET_MODE_PRECISION (mode) - 1)
- return simplify_gen_relational (GE, mode, VOIDmode,
+ && INTVAL (XEXP (op, 1)) == GET_MODE_PRECISION (int_mode) - 1)
+ return simplify_gen_relational (GE, int_mode, VOIDmode,
XEXP (op, 0), const0_rtx);
- if (GET_CODE (op) == SUBREG
+ if (partial_subreg_p (op)
&& subreg_lowpart_p (op)
- && (GET_MODE_SIZE (GET_MODE (op))
- < GET_MODE_SIZE (GET_MODE (SUBREG_REG (op))))
&& GET_CODE (SUBREG_REG (op)) == ASHIFT
&& XEXP (SUBREG_REG (op), 0) == const1_rtx)
{
|| (GET_CODE (false_rtx) == NEG
&& rtx_equal_p (XEXP (false_rtx, 0), true_rtx)))
{
- if (reversed_comparison_code (cond, NULL_RTX) != UNKNOWN)
+ if (reversed_comparison_code (cond, NULL) != UNKNOWN)
temp = reversed_comparison (cond, mode);
else
{
C is equal to the width of MODE minus 1. */
if (GET_CODE (op) == ASHIFTRT
&& CONST_INT_P (XEXP (op, 1))
- && INTVAL (XEXP (op, 1)) == GET_MODE_PRECISION (mode) - 1)
+ && INTVAL (XEXP (op, 1)) == GET_MODE_UNIT_PRECISION (mode) - 1)
return simplify_gen_binary (LSHIFTRT, mode,
XEXP (op, 0), XEXP (op, 1));
C is equal to the width of MODE minus 1. */
if (GET_CODE (op) == LSHIFTRT
&& CONST_INT_P (XEXP (op, 1))
- && INTVAL (XEXP (op, 1)) == GET_MODE_PRECISION (mode) - 1)
+ && INTVAL (XEXP (op, 1)) == GET_MODE_UNIT_PRECISION (mode) - 1)
return simplify_gen_binary (ASHIFTRT, mode,
XEXP (op, 0), XEXP (op, 1));
/* (neg (lt x 0)) is (lshiftrt X C) if STORE_FLAG_VALUE is -1. */
if (GET_CODE (op) == LT
&& XEXP (op, 1) == const0_rtx
- && SCALAR_INT_MODE_P (GET_MODE (XEXP (op, 0))))
+ && is_a <scalar_int_mode> (GET_MODE (XEXP (op, 0)), &inner))
{
- machine_mode inner = GET_MODE (XEXP (op, 0));
+ int_mode = as_a <scalar_int_mode> (mode);
int isize = GET_MODE_PRECISION (inner);
if (STORE_FLAG_VALUE == 1)
{
temp = simplify_gen_binary (ASHIFTRT, inner, XEXP (op, 0),
- GEN_INT (isize - 1));
- if (mode == inner)
+ gen_int_shift_amount (inner,
+ isize - 1));
+ if (int_mode == inner)
return temp;
- if (GET_MODE_PRECISION (mode) > isize)
- return simplify_gen_unary (SIGN_EXTEND, mode, temp, inner);
- return simplify_gen_unary (TRUNCATE, mode, temp, inner);
+ if (GET_MODE_PRECISION (int_mode) > isize)
+ return simplify_gen_unary (SIGN_EXTEND, int_mode, temp, inner);
+ return simplify_gen_unary (TRUNCATE, int_mode, temp, inner);
}
else if (STORE_FLAG_VALUE == -1)
{
temp = simplify_gen_binary (LSHIFTRT, inner, XEXP (op, 0),
- GEN_INT (isize - 1));
- if (mode == inner)
+ gen_int_shift_amount (inner,
+ isize - 1));
+ if (int_mode == inner)
return temp;
- if (GET_MODE_PRECISION (mode) > isize)
- return simplify_gen_unary (ZERO_EXTEND, mode, temp, inner);
- return simplify_gen_unary (TRUNCATE, mode, temp, inner);
+ if (GET_MODE_PRECISION (int_mode) > isize)
+ return simplify_gen_unary (ZERO_EXTEND, int_mode, temp, inner);
+ return simplify_gen_unary (TRUNCATE, int_mode, temp, inner);
+ }
+ }
+
+ if (vec_series_p (op, &base, &step))
+ {
+ /* Only create a new series if we can simplify both parts. In other
+ cases this isn't really a simplification, and it's not necessarily
+ a win to replace a vector operation with a scalar operation. */
+ scalar_mode inner_mode = GET_MODE_INNER (mode);
+ base = simplify_unary_operation (NEG, inner_mode, base, inner_mode);
+ if (base)
+ {
+ step = simplify_unary_operation (NEG, inner_mode,
+ step, inner_mode);
+ if (step)
+ return gen_vec_series (mode, base, step);
}
}
break;
if ((GET_CODE (op) == FLOAT_TRUNCATE
&& flag_unsafe_math_optimizations)
|| GET_CODE (op) == FLOAT_EXTEND)
- return simplify_gen_unary (GET_MODE_SIZE (GET_MODE (XEXP (op,
- 0)))
- > GET_MODE_SIZE (mode)
- ? FLOAT_TRUNCATE : FLOAT_EXTEND,
+ return simplify_gen_unary (GET_MODE_UNIT_SIZE (GET_MODE (XEXP (op, 0)))
+ > GET_MODE_UNIT_SIZE (mode)
+ ? FLOAT_TRUNCATE : FLOAT_EXTEND,
mode,
XEXP (op, 0), mode);
return op;
/* If operand is known to be only -1 or 0, convert ABS to NEG. */
- if (num_sign_bit_copies (op, mode) == GET_MODE_PRECISION (mode))
- return gen_rtx_NEG (mode, op);
+ if (is_a <scalar_int_mode> (mode, &int_mode)
+ && (num_sign_bit_copies (op, int_mode)
+ == GET_MODE_PRECISION (int_mode)))
+ return gen_rtx_NEG (int_mode, op);
break;
if (lcode == ASHIFTRT)
/* Number of bits not shifted off the end. */
- bits = GET_MODE_PRECISION (lmode) - INTVAL (XEXP (lhs, 1));
+ bits = (GET_MODE_UNIT_PRECISION (lmode)
+ - INTVAL (XEXP (lhs, 1)));
else /* lcode == SIGN_EXTEND */
/* Size of inner mode. */
- bits = GET_MODE_PRECISION (GET_MODE (XEXP (lhs, 0)));
+ bits = GET_MODE_UNIT_PRECISION (GET_MODE (XEXP (lhs, 0)));
if (rcode == ASHIFTRT)
- bits += GET_MODE_PRECISION (rmode) - INTVAL (XEXP (rhs, 1));
+ bits += (GET_MODE_UNIT_PRECISION (rmode)
+ - INTVAL (XEXP (rhs, 1)));
else /* rcode == SIGN_EXTEND */
- bits += GET_MODE_PRECISION (GET_MODE (XEXP (rhs, 0)));
+ bits += GET_MODE_UNIT_PRECISION (GET_MODE (XEXP (rhs, 0)));
/* We can only widen multiplies if the result is mathematiclly
equivalent. I.e. if overflow was impossible. */
- if (bits <= GET_MODE_PRECISION (GET_MODE (op)))
+ if (bits <= GET_MODE_UNIT_PRECISION (GET_MODE (op)))
return simplify_gen_binary
(MULT, mode,
simplify_gen_unary (SIGN_EXTEND, mode, lhs, lmode),
if (GET_CODE (op) == SUBREG
&& SUBREG_PROMOTED_VAR_P (op)
&& SUBREG_PROMOTED_SIGNED_P (op)
- && GET_MODE_SIZE (mode) <= GET_MODE_SIZE (GET_MODE (XEXP (op, 0))))
+ && !paradoxical_subreg_p (mode, GET_MODE (SUBREG_REG (op))))
{
temp = rtl_hooks.gen_lowpart_no_emit (mode, op);
if (temp)
(sign_extend:M (zero_extend:N <X>)) is (zero_extend:M <X>). */
if (GET_CODE (op) == SIGN_EXTEND || GET_CODE (op) == ZERO_EXTEND)
{
- gcc_assert (GET_MODE_PRECISION (mode)
- > GET_MODE_PRECISION (GET_MODE (op)));
+ gcc_assert (GET_MODE_UNIT_PRECISION (mode)
+ > GET_MODE_UNIT_PRECISION (GET_MODE (op)));
return simplify_gen_unary (GET_CODE (op), mode, XEXP (op, 0),
GET_MODE (XEXP (op, 0)));
}
is similarly (zero_extend:M (subreg:O <X>)). */
if ((GET_CODE (op) == ASHIFTRT || GET_CODE (op) == LSHIFTRT)
&& GET_CODE (XEXP (op, 0)) == ASHIFT
+ && is_a <scalar_int_mode> (mode, &int_mode)
&& CONST_INT_P (XEXP (op, 1))
&& XEXP (XEXP (op, 0), 1) == XEXP (op, 1)
- && GET_MODE_BITSIZE (GET_MODE (op)) > INTVAL (XEXP (op, 1)))
- {
- machine_mode tmode
- = mode_for_size (GET_MODE_BITSIZE (GET_MODE (op))
- - INTVAL (XEXP (op, 1)), MODE_INT, 1);
- gcc_assert (GET_MODE_BITSIZE (mode)
- > GET_MODE_BITSIZE (GET_MODE (op)));
- if (tmode != BLKmode)
+ && (op_mode = as_a <scalar_int_mode> (GET_MODE (op)),
+ GET_MODE_BITSIZE (op_mode) > INTVAL (XEXP (op, 1))))
+ {
+ scalar_int_mode tmode;
+ gcc_assert (GET_MODE_BITSIZE (int_mode)
+ > GET_MODE_BITSIZE (op_mode));
+ if (int_mode_for_size (GET_MODE_BITSIZE (op_mode)
+ - INTVAL (XEXP (op, 1)), 1).exists (&tmode))
{
rtx inner =
rtl_hooks.gen_lowpart_no_emit (tmode, XEXP (XEXP (op, 0), 0));
if (inner)
return simplify_gen_unary (GET_CODE (op) == ASHIFTRT
? SIGN_EXTEND : ZERO_EXTEND,
- mode, inner, tmode);
+ int_mode, inner, tmode);
}
}
+ /* (sign_extend:M (lshiftrt:N <X> (const_int I))) is better as
+ (zero_extend:M (lshiftrt:N <X> (const_int I))) if I is not 0. */
+ if (GET_CODE (op) == LSHIFTRT
+ && CONST_INT_P (XEXP (op, 1))
+ && XEXP (op, 1) != const0_rtx)
+ return simplify_gen_unary (ZERO_EXTEND, mode, op, GET_MODE (op));
+
#if defined(POINTERS_EXTEND_UNSIGNED)
/* As we do not know which address space the pointer is referring to,
we can do this only if the target does not support different pointer
&& REG_POINTER (SUBREG_REG (op))
&& GET_MODE (SUBREG_REG (op)) == Pmode))
&& !targetm.have_ptr_extend ())
- return convert_memory_address (Pmode, op);
+ {
+ temp
+ = convert_memory_address_addr_space_1 (Pmode, op,
+ ADDR_SPACE_GENERIC, false,
+ true);
+ if (temp)
+ return temp;
+ }
#endif
break;
if (GET_CODE (op) == SUBREG
&& SUBREG_PROMOTED_VAR_P (op)
&& SUBREG_PROMOTED_UNSIGNED_P (op)
- && GET_MODE_SIZE (mode) <= GET_MODE_SIZE (GET_MODE (XEXP (op, 0))))
+ && !paradoxical_subreg_p (mode, GET_MODE (SUBREG_REG (op))))
{
temp = rtl_hooks.gen_lowpart_no_emit (mode, op);
if (temp)
if (lcode == LSHIFTRT)
/* Number of bits not shifted off the end. */
- bits = GET_MODE_PRECISION (lmode) - INTVAL (XEXP (lhs, 1));
+ bits = (GET_MODE_UNIT_PRECISION (lmode)
+ - INTVAL (XEXP (lhs, 1)));
else /* lcode == ZERO_EXTEND */
/* Size of inner mode. */
- bits = GET_MODE_PRECISION (GET_MODE (XEXP (lhs, 0)));
+ bits = GET_MODE_UNIT_PRECISION (GET_MODE (XEXP (lhs, 0)));
if (rcode == LSHIFTRT)
- bits += GET_MODE_PRECISION (rmode) - INTVAL (XEXP (rhs, 1));
+ bits += (GET_MODE_UNIT_PRECISION (rmode)
+ - INTVAL (XEXP (rhs, 1)));
else /* rcode == ZERO_EXTEND */
- bits += GET_MODE_PRECISION (GET_MODE (XEXP (rhs, 0)));
+ bits += GET_MODE_UNIT_PRECISION (GET_MODE (XEXP (rhs, 0)));
/* We can only widen multiplies if the result is mathematiclly
equivalent. I.e. if overflow was impossible. */
- if (bits <= GET_MODE_PRECISION (GET_MODE (op)))
+ if (bits <= GET_MODE_UNIT_PRECISION (GET_MODE (op)))
return simplify_gen_binary
(MULT, mode,
simplify_gen_unary (ZERO_EXTEND, mode, lhs, lmode),
GET_MODE_PRECISION (N) - I bits. */
if (GET_CODE (op) == LSHIFTRT
&& GET_CODE (XEXP (op, 0)) == ASHIFT
+ && is_a <scalar_int_mode> (mode, &int_mode)
&& CONST_INT_P (XEXP (op, 1))
&& XEXP (XEXP (op, 0), 1) == XEXP (op, 1)
- && GET_MODE_PRECISION (GET_MODE (op)) > INTVAL (XEXP (op, 1)))
+ && (op_mode = as_a <scalar_int_mode> (GET_MODE (op)),
+ GET_MODE_PRECISION (op_mode) > INTVAL (XEXP (op, 1))))
{
- machine_mode tmode
- = mode_for_size (GET_MODE_PRECISION (GET_MODE (op))
- - INTVAL (XEXP (op, 1)), MODE_INT, 1);
- if (tmode != BLKmode)
+ scalar_int_mode tmode;
+ if (int_mode_for_size (GET_MODE_PRECISION (op_mode)
+ - INTVAL (XEXP (op, 1)), 1).exists (&tmode))
{
rtx inner =
rtl_hooks.gen_lowpart_no_emit (tmode, XEXP (XEXP (op, 0), 0));
if (inner)
- return simplify_gen_unary (ZERO_EXTEND, mode, inner, tmode);
+ return simplify_gen_unary (ZERO_EXTEND, int_mode,
+ inner, tmode);
}
}
of mode N. E.g.
(zero_extend:SI (subreg:QI (and:SI (reg:SI) (const_int 63)) 0)) is
(and:SI (reg:SI) (const_int 63)). */
- if (GET_CODE (op) == SUBREG
- && GET_MODE_PRECISION (GET_MODE (op))
- < GET_MODE_PRECISION (GET_MODE (SUBREG_REG (op)))
- && GET_MODE_PRECISION (GET_MODE (SUBREG_REG (op)))
- <= HOST_BITS_PER_WIDE_INT
- && GET_MODE_PRECISION (mode)
- >= GET_MODE_PRECISION (GET_MODE (SUBREG_REG (op)))
+ if (partial_subreg_p (op)
+ && is_a <scalar_int_mode> (mode, &int_mode)
+ && is_a <scalar_int_mode> (GET_MODE (SUBREG_REG (op)), &op0_mode)
+ && GET_MODE_PRECISION (op0_mode) <= HOST_BITS_PER_WIDE_INT
+ && GET_MODE_PRECISION (int_mode) >= GET_MODE_PRECISION (op0_mode)
&& subreg_lowpart_p (op)
- && (nonzero_bits (SUBREG_REG (op), GET_MODE (SUBREG_REG (op)))
+ && (nonzero_bits (SUBREG_REG (op), op0_mode)
& ~GET_MODE_MASK (GET_MODE (op))) == 0)
{
- if (GET_MODE_PRECISION (mode)
- == GET_MODE_PRECISION (GET_MODE (SUBREG_REG (op))))
+ if (GET_MODE_PRECISION (int_mode) == GET_MODE_PRECISION (op0_mode))
return SUBREG_REG (op);
- return simplify_gen_unary (ZERO_EXTEND, mode, SUBREG_REG (op),
- GET_MODE (SUBREG_REG (op)));
+ return simplify_gen_unary (ZERO_EXTEND, int_mode, SUBREG_REG (op),
+ op0_mode);
}
#if defined(POINTERS_EXTEND_UNSIGNED)
&& REG_POINTER (SUBREG_REG (op))
&& GET_MODE (SUBREG_REG (op)) == Pmode))
&& !targetm.have_ptr_extend ())
- return convert_memory_address (Pmode, op);
+ {
+ temp
+ = convert_memory_address_addr_space_1 (Pmode, op,
+ ADDR_SPACE_GENERIC, false,
+ true);
+ if (temp)
+ return temp;
+ }
#endif
break;
break;
}
+ if (VECTOR_MODE_P (mode) && vec_duplicate_p (op, &elt))
+ {
+ /* Try applying the operator to ELT and see if that simplifies.
+ We can duplicate the result if so.
+
+ The reason we don't use simplify_gen_unary is that it isn't
+ necessarily a win to convert things like:
+
+ (neg:V (vec_duplicate:V (reg:S R)))
+
+ to:
+
+ (vec_duplicate:V (neg:S (reg:S R)))
+
+ The first might be done entirely in vector registers while the
+ second might need a move between register files. */
+ temp = simplify_unary_operation (code, GET_MODE_INNER (mode),
+ elt, GET_MODE_INNER (GET_MODE (op)));
+ if (temp)
+ return gen_vec_duplicate (mode, temp);
+ }
+
return 0;
}
simplify_const_unary_operation (enum rtx_code code, machine_mode mode,
rtx op, machine_mode op_mode)
{
- unsigned int width = GET_MODE_PRECISION (mode);
+ scalar_int_mode result_mode;
if (code == VEC_DUPLICATE)
{
gcc_assert (GET_MODE_INNER (mode) == GET_MODE_INNER
(GET_MODE (op)));
}
- if (CONST_SCALAR_INT_P (op) || CONST_DOUBLE_AS_FLOAT_P (op)
- || GET_CODE (op) == CONST_VECTOR)
+ if (CONST_SCALAR_INT_P (op) || CONST_DOUBLE_AS_FLOAT_P (op))
+ return gen_const_vec_duplicate (mode, op);
+ if (GET_CODE (op) == CONST_VECTOR)
{
- int elt_size = GET_MODE_UNIT_SIZE (mode);
- unsigned n_elts = (GET_MODE_SIZE (mode) / elt_size);
+ unsigned int n_elts = GET_MODE_NUNITS (mode);
+ unsigned int in_n_elts = CONST_VECTOR_NUNITS (op);
+ gcc_assert (in_n_elts < n_elts);
+ gcc_assert ((n_elts % in_n_elts) == 0);
rtvec v = rtvec_alloc (n_elts);
- unsigned int i;
-
- if (GET_CODE (op) != CONST_VECTOR)
- for (i = 0; i < n_elts; i++)
- RTVEC_ELT (v, i) = op;
- else
- {
- machine_mode inmode = GET_MODE (op);
- int in_elt_size = GET_MODE_UNIT_SIZE (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 (op, i % in_n_elts);
- }
+ for (unsigned i = 0; i < n_elts; i++)
+ RTVEC_ELT (v, i) = CONST_VECTOR_ELT (op, i % in_n_elts);
return gen_rtx_CONST_VECTOR (mode, v);
}
}
op_mode = MAX_MODE_INT;
}
- real_from_integer (&d, mode, std::make_pair (op, op_mode), SIGNED);
+ real_from_integer (&d, mode, rtx_mode_t (op, op_mode), SIGNED);
+
+ /* Avoid the folding if flag_signaling_nans is on and
+ operand is a signaling NaN. */
+ if (HONOR_SNANS (mode) && REAL_VALUE_ISSIGNALING_NAN (d))
+ return 0;
+
d = real_value_truncate (mode, d);
return const_double_from_real_value (d, mode);
}
op_mode = MAX_MODE_INT;
}
- real_from_integer (&d, mode, std::make_pair (op, op_mode), UNSIGNED);
+ real_from_integer (&d, mode, rtx_mode_t (op, op_mode), UNSIGNED);
+
+ /* Avoid the folding if flag_signaling_nans is on and
+ operand is a signaling NaN. */
+ if (HONOR_SNANS (mode) && REAL_VALUE_ISSIGNALING_NAN (d))
+ return 0;
+
d = real_value_truncate (mode, d);
return const_double_from_real_value (d, mode);
}
- if (CONST_SCALAR_INT_P (op) && width > 0)
+ if (CONST_SCALAR_INT_P (op) && is_a <scalar_int_mode> (mode, &result_mode))
{
+ unsigned int width = GET_MODE_PRECISION (result_mode);
wide_int result;
- machine_mode imode = op_mode == VOIDmode ? mode : op_mode;
- rtx_mode_t op0 = std::make_pair (op, imode);
+ scalar_int_mode imode = (op_mode == VOIDmode
+ ? result_mode
+ : as_a <scalar_int_mode> (op_mode));
+ rtx_mode_t op0 = rtx_mode_t (op, imode);
int int_value;
#if TARGET_SUPPORTS_WIDE_INT == 0
break;
case FFS:
- result = wi::shwi (wi::ffs (op0), mode);
+ result = wi::shwi (wi::ffs (op0), result_mode);
break;
case CLZ:
if (wi::ne_p (op0, 0))
int_value = wi::clz (op0);
- else if (! CLZ_DEFINED_VALUE_AT_ZERO (mode, int_value))
- int_value = GET_MODE_PRECISION (mode);
- result = wi::shwi (int_value, mode);
+ else if (! CLZ_DEFINED_VALUE_AT_ZERO (imode, int_value))
+ int_value = GET_MODE_PRECISION (imode);
+ result = wi::shwi (int_value, result_mode);
break;
case CLRSB:
- result = wi::shwi (wi::clrsb (op0), mode);
+ result = wi::shwi (wi::clrsb (op0), result_mode);
break;
case CTZ:
if (wi::ne_p (op0, 0))
int_value = wi::ctz (op0);
- else if (! CTZ_DEFINED_VALUE_AT_ZERO (mode, int_value))
- int_value = GET_MODE_PRECISION (mode);
- result = wi::shwi (int_value, mode);
+ else if (! CTZ_DEFINED_VALUE_AT_ZERO (imode, int_value))
+ int_value = GET_MODE_PRECISION (imode);
+ result = wi::shwi (int_value, result_mode);
break;
case POPCOUNT:
- result = wi::shwi (wi::popcount (op0), mode);
+ result = wi::shwi (wi::popcount (op0), result_mode);
break;
case PARITY:
- result = wi::shwi (wi::parity (op0), mode);
+ result = wi::shwi (wi::parity (op0), result_mode);
break;
case BSWAP:
return 0;
}
- return immed_wide_int_const (result, mode);
+ return immed_wide_int_const (result, result_mode);
}
else if (CONST_DOUBLE_AS_FLOAT_P (op)
d = real_value_negate (&d);
break;
case FLOAT_TRUNCATE:
+ /* Don't perform the operation if flag_signaling_nans is on
+ and the operand is a signaling NaN. */
+ if (HONOR_SNANS (mode) && REAL_VALUE_ISSIGNALING_NAN (d))
+ return NULL_RTX;
d = real_value_truncate (mode, d);
break;
case FLOAT_EXTEND:
+ /* Don't perform the operation if flag_signaling_nans is on
+ and the operand is a signaling NaN. */
+ if (HONOR_SNANS (mode) && REAL_VALUE_ISSIGNALING_NAN (d))
+ return NULL_RTX;
/* All this does is change the mode, unless changing
mode class. */
if (GET_MODE_CLASS (mode) != GET_MODE_CLASS (GET_MODE (op)))
real_convert (&d, mode, &d);
break;
case FIX:
+ /* Don't perform the operation if flag_signaling_nans is on
+ and the operand is a signaling NaN. */
+ if (HONOR_SNANS (mode) && REAL_VALUE_ISSIGNALING_NAN (d))
+ return NULL_RTX;
real_arithmetic (&d, FIX_TRUNC_EXPR, &d, NULL);
break;
case NOT:
}
else if (CONST_DOUBLE_AS_FLOAT_P (op)
&& SCALAR_FLOAT_MODE_P (GET_MODE (op))
- && GET_MODE_CLASS (mode) == MODE_INT
- && width > 0)
+ && is_int_mode (mode, &result_mode))
{
+ unsigned int width = GET_MODE_PRECISION (result_mode);
/* Although the overflow semantics of RTL's FIX and UNSIGNED_FIX
operators are intentionally left unspecified (to ease implementation
by target backends), for consistency, this routine implements the
}
}
+ /* Handle polynomial integers. */
+ else if (CONST_POLY_INT_P (op))
+ {
+ poly_wide_int result;
+ switch (code)
+ {
+ case NEG:
+ result = -const_poly_int_value (op);
+ break;
+
+ case NOT:
+ result = ~const_poly_int_value (op);
+ break;
+
+ default:
+ return NULL_RTX;
+ }
+ return immed_wide_int_const (result, mode);
+ }
+
return NULL_RTX;
}
\f
return NULL_RTX;
}
+/* Subroutine of simplify_binary_operation_1 that looks for cases in
+ which OP0 and OP1 are both vector series or vector duplicates
+ (which are really just series with a step of 0). If so, try to
+ form a new series by applying CODE to the bases and to the steps.
+ Return null if no simplification is possible.
+
+ MODE is the mode of the operation and is known to be a vector
+ integer mode. */
+
+static rtx
+simplify_binary_operation_series (rtx_code code, machine_mode mode,
+ rtx op0, rtx op1)
+{
+ rtx base0, step0;
+ if (vec_duplicate_p (op0, &base0))
+ step0 = const0_rtx;
+ else if (!vec_series_p (op0, &base0, &step0))
+ return NULL_RTX;
+
+ rtx base1, step1;
+ if (vec_duplicate_p (op1, &base1))
+ step1 = const0_rtx;
+ else if (!vec_series_p (op1, &base1, &step1))
+ return NULL_RTX;
+
+ /* Only create a new series if we can simplify both parts. In other
+ cases this isn't really a simplification, and it's not necessarily
+ a win to replace a vector operation with a scalar operation. */
+ scalar_mode inner_mode = GET_MODE_INNER (mode);
+ rtx new_base = simplify_binary_operation (code, inner_mode, base0, base1);
+ if (!new_base)
+ return NULL_RTX;
+
+ rtx new_step = simplify_binary_operation (code, inner_mode, step0, step1);
+ if (!new_step)
+ return NULL_RTX;
+
+ return gen_vec_series (mode, new_base, new_step);
+}
+
/* Subroutine of simplify_binary_operation. Simplify a binary operation
CODE with result mode MODE, operating on OP0 and OP1. If OP0 and/or
OP1 are constant pool references, TRUEOP0 and TRUEOP1 represent the
simplify_binary_operation_1 (enum rtx_code code, machine_mode mode,
rtx op0, rtx op1, rtx trueop0, rtx trueop1)
{
- rtx tem, reversed, opleft, opright;
+ rtx tem, reversed, opleft, opright, elt0, elt1;
HOST_WIDE_INT val;
- unsigned int width = GET_MODE_PRECISION (mode);
+ scalar_int_mode int_mode, inner_mode;
+ poly_int64 offset;
/* Even if we can't compute a constant result,
there are some cases worth simplifying. */
have X (if C is 2 in the example above). But don't make
something more expensive than we had before. */
- if (SCALAR_INT_MODE_P (mode))
+ if (is_a <scalar_int_mode> (mode, &int_mode))
{
rtx lhs = op0, rhs = op1;
- wide_int coeff0 = wi::one (GET_MODE_PRECISION (mode));
- wide_int coeff1 = wi::one (GET_MODE_PRECISION (mode));
+ wide_int coeff0 = wi::one (GET_MODE_PRECISION (int_mode));
+ wide_int coeff1 = wi::one (GET_MODE_PRECISION (int_mode));
if (GET_CODE (lhs) == NEG)
{
- coeff0 = wi::minus_one (GET_MODE_PRECISION (mode));
+ coeff0 = wi::minus_one (GET_MODE_PRECISION (int_mode));
lhs = XEXP (lhs, 0);
}
else if (GET_CODE (lhs) == MULT
&& CONST_SCALAR_INT_P (XEXP (lhs, 1)))
{
- coeff0 = std::make_pair (XEXP (lhs, 1), mode);
+ coeff0 = rtx_mode_t (XEXP (lhs, 1), int_mode);
lhs = XEXP (lhs, 0);
}
else if (GET_CODE (lhs) == ASHIFT
&& CONST_INT_P (XEXP (lhs, 1))
&& INTVAL (XEXP (lhs, 1)) >= 0
- && INTVAL (XEXP (lhs, 1)) < GET_MODE_PRECISION (mode))
+ && INTVAL (XEXP (lhs, 1)) < GET_MODE_PRECISION (int_mode))
{
coeff0 = wi::set_bit_in_zero (INTVAL (XEXP (lhs, 1)),
- GET_MODE_PRECISION (mode));
+ GET_MODE_PRECISION (int_mode));
lhs = XEXP (lhs, 0);
}
if (GET_CODE (rhs) == NEG)
{
- coeff1 = wi::minus_one (GET_MODE_PRECISION (mode));
+ coeff1 = wi::minus_one (GET_MODE_PRECISION (int_mode));
rhs = XEXP (rhs, 0);
}
else if (GET_CODE (rhs) == MULT
&& CONST_INT_P (XEXP (rhs, 1)))
{
- coeff1 = std::make_pair (XEXP (rhs, 1), mode);
+ coeff1 = rtx_mode_t (XEXP (rhs, 1), int_mode);
rhs = XEXP (rhs, 0);
}
else if (GET_CODE (rhs) == ASHIFT
&& CONST_INT_P (XEXP (rhs, 1))
&& INTVAL (XEXP (rhs, 1)) >= 0
- && INTVAL (XEXP (rhs, 1)) < GET_MODE_PRECISION (mode))
+ && INTVAL (XEXP (rhs, 1)) < GET_MODE_PRECISION (int_mode))
{
coeff1 = wi::set_bit_in_zero (INTVAL (XEXP (rhs, 1)),
- GET_MODE_PRECISION (mode));
+ GET_MODE_PRECISION (int_mode));
rhs = XEXP (rhs, 0);
}
if (rtx_equal_p (lhs, rhs))
{
- rtx orig = gen_rtx_PLUS (mode, op0, op1);
+ rtx orig = gen_rtx_PLUS (int_mode, op0, op1);
rtx coeff;
bool speed = optimize_function_for_speed_p (cfun);
- coeff = immed_wide_int_const (coeff0 + coeff1, mode);
+ coeff = immed_wide_int_const (coeff0 + coeff1, int_mode);
- tem = simplify_gen_binary (MULT, mode, lhs, coeff);
- return (set_src_cost (tem, mode, speed)
- <= set_src_cost (orig, mode, speed) ? tem : 0);
+ tem = simplify_gen_binary (MULT, int_mode, lhs, coeff);
+ return (set_src_cost (tem, int_mode, speed)
+ <= set_src_cost (orig, int_mode, speed) ? tem : 0);
}
}
if (tem)
return tem;
}
+
+ /* Handle vector series. */
+ if (GET_MODE_CLASS (mode) == MODE_VECTOR_INT)
+ {
+ tem = simplify_binary_operation_series (code, mode, op0, op1);
+ if (tem)
+ return tem;
+ }
break;
case COMPARE:
return xop00;
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)
+ && GET_MODE (xop00) == mode
+ && GET_MODE (xop10) == mode
+ && GET_MODE_CLASS (mode) == MODE_CC)
return xop00;
}
break;
if (!HONOR_SIGNED_ZEROS (mode) && trueop0 == CONST0_RTX (mode))
return simplify_gen_unary (NEG, mode, op1, mode);
- /* (-1 - a) is ~a. */
- if (trueop0 == constm1_rtx)
+ /* (-1 - a) is ~a, unless the expression contains symbolic
+ constants, in which case not retaining additions and
+ subtractions could cause invalid assembly to be produced. */
+ if (trueop0 == constm1_rtx
+ && !contains_symbolic_reference_p (op1))
return simplify_gen_unary (NOT, mode, op1, mode);
/* Subtracting 0 has no effect unless the mode has signed zeros
have X (if C is 2 in the example above). But don't make
something more expensive than we had before. */
- if (SCALAR_INT_MODE_P (mode))
+ if (is_a <scalar_int_mode> (mode, &int_mode))
{
rtx lhs = op0, rhs = op1;
- wide_int coeff0 = wi::one (GET_MODE_PRECISION (mode));
- wide_int negcoeff1 = wi::minus_one (GET_MODE_PRECISION (mode));
+ wide_int coeff0 = wi::one (GET_MODE_PRECISION (int_mode));
+ wide_int negcoeff1 = wi::minus_one (GET_MODE_PRECISION (int_mode));
if (GET_CODE (lhs) == NEG)
{
- coeff0 = wi::minus_one (GET_MODE_PRECISION (mode));
+ coeff0 = wi::minus_one (GET_MODE_PRECISION (int_mode));
lhs = XEXP (lhs, 0);
}
else if (GET_CODE (lhs) == MULT
&& CONST_SCALAR_INT_P (XEXP (lhs, 1)))
{
- coeff0 = std::make_pair (XEXP (lhs, 1), mode);
+ coeff0 = rtx_mode_t (XEXP (lhs, 1), int_mode);
lhs = XEXP (lhs, 0);
}
else if (GET_CODE (lhs) == ASHIFT
&& CONST_INT_P (XEXP (lhs, 1))
&& INTVAL (XEXP (lhs, 1)) >= 0
- && INTVAL (XEXP (lhs, 1)) < GET_MODE_PRECISION (mode))
+ && INTVAL (XEXP (lhs, 1)) < GET_MODE_PRECISION (int_mode))
{
coeff0 = wi::set_bit_in_zero (INTVAL (XEXP (lhs, 1)),
- GET_MODE_PRECISION (mode));
+ GET_MODE_PRECISION (int_mode));
lhs = XEXP (lhs, 0);
}
if (GET_CODE (rhs) == NEG)
{
- negcoeff1 = wi::one (GET_MODE_PRECISION (mode));
+ negcoeff1 = wi::one (GET_MODE_PRECISION (int_mode));
rhs = XEXP (rhs, 0);
}
else if (GET_CODE (rhs) == MULT
&& CONST_INT_P (XEXP (rhs, 1)))
{
- negcoeff1 = wi::neg (std::make_pair (XEXP (rhs, 1), mode));
+ negcoeff1 = wi::neg (rtx_mode_t (XEXP (rhs, 1), int_mode));
rhs = XEXP (rhs, 0);
}
else if (GET_CODE (rhs) == ASHIFT
&& CONST_INT_P (XEXP (rhs, 1))
&& INTVAL (XEXP (rhs, 1)) >= 0
- && INTVAL (XEXP (rhs, 1)) < GET_MODE_PRECISION (mode))
+ && INTVAL (XEXP (rhs, 1)) < GET_MODE_PRECISION (int_mode))
{
negcoeff1 = wi::set_bit_in_zero (INTVAL (XEXP (rhs, 1)),
- GET_MODE_PRECISION (mode));
+ GET_MODE_PRECISION (int_mode));
negcoeff1 = -negcoeff1;
rhs = XEXP (rhs, 0);
}
if (rtx_equal_p (lhs, rhs))
{
- rtx orig = gen_rtx_MINUS (mode, op0, op1);
+ rtx orig = gen_rtx_MINUS (int_mode, op0, op1);
rtx coeff;
bool speed = optimize_function_for_speed_p (cfun);
- coeff = immed_wide_int_const (coeff0 + negcoeff1, mode);
+ coeff = immed_wide_int_const (coeff0 + negcoeff1, int_mode);
- tem = simplify_gen_binary (MULT, mode, lhs, coeff);
- return (set_src_cost (tem, mode, speed)
- <= set_src_cost (orig, mode, speed) ? tem : 0);
+ tem = simplify_gen_binary (MULT, int_mode, lhs, coeff);
+ return (set_src_cost (tem, int_mode, speed)
+ <= set_src_cost (orig, int_mode, speed) ? tem : 0);
}
}
return simplify_gen_binary (MINUS, mode, tem, XEXP (op0, 0));
}
+ if ((GET_CODE (op0) == CONST
+ || GET_CODE (op0) == SYMBOL_REF
+ || GET_CODE (op0) == LABEL_REF)
+ && poly_int_rtx_p (op1, &offset))
+ return plus_constant (mode, op0, trunc_int_for_mode (-offset, mode));
+
/* Don't let a relocatable value get a negative coeff. */
if (CONST_INT_P (op1) && GET_MODE (op0) != VOIDmode)
return simplify_gen_binary (PLUS, mode,
|| plus_minus_operand_p (op1))
&& (tem = simplify_plus_minus (code, mode, op0, op1)) != 0)
return tem;
+
+ /* Handle vector series. */
+ if (GET_MODE_CLASS (mode) == MODE_VECTOR_INT)
+ {
+ tem = simplify_binary_operation_series (code, mode, op0, op1);
+ if (tem)
+ return tem;
+ }
break;
case MULT:
/* Convert multiply by constant power of two into shift. */
if (CONST_SCALAR_INT_P (trueop1))
{
- val = wi::exact_log2 (std::make_pair (trueop1, mode));
+ val = wi::exact_log2 (rtx_mode_t (trueop1, mode));
if (val >= 0)
- return simplify_gen_binary (ASHIFT, mode, op0, GEN_INT (val));
+ return simplify_gen_binary (ASHIFT, mode, op0,
+ gen_int_shift_amount (mode, val));
}
/* x*2 is x+x and x*(-1) is -x */
HOST_WIDE_INT c1 = INTVAL (XEXP (op0, 1));
HOST_WIDE_INT c2 = INTVAL (trueop1);
- /* If (C1&C2) == C1, then (X&C1)|C2 becomes X. */
+ /* If (C1&C2) == C1, then (X&C1)|C2 becomes C2. */
if ((c1 & c2) == c1
&& !side_effects_p (XEXP (op0, 0)))
return trueop1;
/* If (C1|C2) == ~0 then (X&C1)|C2 becomes X|C2. */
if (((c1|c2) & mask) == mask)
return simplify_gen_binary (IOR, mode, XEXP (op0, 0), op1);
-
- /* Minimize the number of bits set in C1, i.e. C1 := C1 & ~C2. */
- if (((c1 & ~c2) & mask) != (c1 & mask))
- {
- tem = simplify_gen_binary (AND, mode, XEXP (op0, 0),
- gen_int_mode (c1 & ~c2, mode));
- return simplify_gen_binary (IOR, mode, tem, op1);
- }
}
/* Convert (A & B) | A to A. */
&& CONST_INT_P (XEXP (opleft, 1))
&& CONST_INT_P (XEXP (opright, 1))
&& (INTVAL (XEXP (opleft, 1)) + INTVAL (XEXP (opright, 1))
- == GET_MODE_PRECISION (mode)))
+ == GET_MODE_UNIT_PRECISION (mode)))
return gen_rtx_ROTATE (mode, XEXP (opright, 0), XEXP (opleft, 1));
/* Same, but for ashift that has been "simplified" to a wider mode
by simplify_shift_const. */
if (GET_CODE (opleft) == SUBREG
+ && is_a <scalar_int_mode> (mode, &int_mode)
+ && is_a <scalar_int_mode> (GET_MODE (SUBREG_REG (opleft)),
+ &inner_mode)
&& GET_CODE (SUBREG_REG (opleft)) == ASHIFT
&& GET_CODE (opright) == LSHIFTRT
&& GET_CODE (XEXP (opright, 0)) == SUBREG
- && GET_MODE (opleft) == GET_MODE (XEXP (opright, 0))
- && SUBREG_BYTE (opleft) == SUBREG_BYTE (XEXP (opright, 0))
- && (GET_MODE_SIZE (GET_MODE (opleft))
- < GET_MODE_SIZE (GET_MODE (SUBREG_REG (opleft))))
+ && known_eq (SUBREG_BYTE (opleft), SUBREG_BYTE (XEXP (opright, 0)))
+ && GET_MODE_SIZE (int_mode) < GET_MODE_SIZE (inner_mode)
&& rtx_equal_p (XEXP (SUBREG_REG (opleft), 0),
SUBREG_REG (XEXP (opright, 0)))
&& CONST_INT_P (XEXP (SUBREG_REG (opleft), 1))
&& CONST_INT_P (XEXP (opright, 1))
- && (INTVAL (XEXP (SUBREG_REG (opleft), 1)) + INTVAL (XEXP (opright, 1))
- == GET_MODE_PRECISION (mode)))
- return gen_rtx_ROTATE (mode, XEXP (opright, 0),
- XEXP (SUBREG_REG (opleft), 1));
-
- /* If we have (ior (and (X C1) C2)), simplify this by making
- C1 as small as possible if C1 actually changes. */
- if (CONST_INT_P (op1)
- && (HWI_COMPUTABLE_MODE_P (mode)
- || INTVAL (op1) > 0)
- && GET_CODE (op0) == AND
- && CONST_INT_P (XEXP (op0, 1))
- && CONST_INT_P (op1)
- && (UINTVAL (XEXP (op0, 1)) & UINTVAL (op1)) != 0)
- {
- rtx tmp = simplify_gen_binary (AND, mode, XEXP (op0, 0),
- gen_int_mode (UINTVAL (XEXP (op0, 1))
- & ~UINTVAL (op1),
- mode));
- return simplify_gen_binary (IOR, mode, tmp, op1);
- }
+ && (INTVAL (XEXP (SUBREG_REG (opleft), 1))
+ + INTVAL (XEXP (opright, 1))
+ == GET_MODE_PRECISION (int_mode)))
+ return gen_rtx_ROTATE (int_mode, XEXP (opright, 0),
+ XEXP (SUBREG_REG (opleft), 1));
/* If OP0 is (ashiftrt (plus ...) C), it might actually be
a (sign_extend (plus ...)). Then check if OP1 is a CONST_INT and
&& CONST_INT_P (XEXP (op0, 1))
&& INTVAL (XEXP (op0, 1)) < HOST_BITS_PER_WIDE_INT)
{
- int count = INTVAL (XEXP (op0, 1));
- HOST_WIDE_INT mask = INTVAL (trueop1) << count;
+ int count = INTVAL (XEXP (op0, 1));
+ HOST_WIDE_INT mask = UINTVAL (trueop1) << count;
if (mask >> count == INTVAL (trueop1)
&& trunc_int_for_mode (mask, mode) == mask
}
}
+ /* If we have (xor (and (xor A B) C) A) with C a constant we can instead
+ do (ior (and A ~C) (and B C)) which is a machine instruction on some
+ machines, and also has shorter instruction path length. */
+ if (GET_CODE (op0) == AND
+ && GET_CODE (XEXP (op0, 0)) == XOR
+ && CONST_INT_P (XEXP (op0, 1))
+ && rtx_equal_p (XEXP (XEXP (op0, 0), 0), trueop1))
+ {
+ rtx a = trueop1;
+ rtx b = XEXP (XEXP (op0, 0), 1);
+ rtx c = XEXP (op0, 1);
+ rtx nc = simplify_gen_unary (NOT, mode, c, mode);
+ rtx a_nc = simplify_gen_binary (AND, mode, a, nc);
+ rtx bc = simplify_gen_binary (AND, mode, b, c);
+ return simplify_gen_binary (IOR, mode, a_nc, bc);
+ }
+ /* Similarly, (xor (and (xor A B) C) B) as (ior (and A C) (and B ~C)) */
+ else if (GET_CODE (op0) == AND
+ && GET_CODE (XEXP (op0, 0)) == XOR
+ && CONST_INT_P (XEXP (op0, 1))
+ && rtx_equal_p (XEXP (XEXP (op0, 0), 1), trueop1))
+ {
+ rtx a = XEXP (XEXP (op0, 0), 0);
+ rtx b = trueop1;
+ rtx c = XEXP (op0, 1);
+ rtx nc = simplify_gen_unary (NOT, mode, c, mode);
+ rtx b_nc = simplify_gen_binary (AND, mode, b, nc);
+ rtx ac = simplify_gen_binary (AND, mode, a, c);
+ return simplify_gen_binary (IOR, mode, ac, b_nc);
+ }
+
/* (xor (comparison foo bar) (const_int 1)) can become the reversed
comparison if STORE_FLAG_VALUE is 1. */
if (STORE_FLAG_VALUE == 1
is (lt foo (const_int 0)), so we can perform the above
simplification if STORE_FLAG_VALUE is 1. */
- if (STORE_FLAG_VALUE == 1
+ if (is_a <scalar_int_mode> (mode, &int_mode)
+ && STORE_FLAG_VALUE == 1
&& trueop1 == const1_rtx
&& GET_CODE (op0) == LSHIFTRT
&& CONST_INT_P (XEXP (op0, 1))
- && INTVAL (XEXP (op0, 1)) == GET_MODE_PRECISION (mode) - 1)
- return gen_rtx_GE (mode, XEXP (op0, 0), const0_rtx);
+ && INTVAL (XEXP (op0, 1)) == GET_MODE_PRECISION (int_mode) - 1)
+ return gen_rtx_GE (int_mode, XEXP (op0, 0), const0_rtx);
/* (xor (comparison foo bar) (const_int sign-bit))
when STORE_FLAG_VALUE is the sign bit. */
- if (val_signbit_p (mode, STORE_FLAG_VALUE)
+ if (is_a <scalar_int_mode> (mode, &int_mode)
+ && val_signbit_p (int_mode, STORE_FLAG_VALUE)
&& trueop1 == const_true_rtx
&& COMPARISON_P (op0)
- && (reversed = reversed_comparison (op0, mode)))
+ && (reversed = reversed_comparison (op0, int_mode)))
return reversed;
tem = simplify_byte_swapping_operation (code, mode, op0, op1);
case UDIV:
/* 0/x is 0 (or x&0 if x has side-effects). */
- if (trueop0 == CONST0_RTX (mode))
+ if (trueop0 == CONST0_RTX (mode)
+ && !cfun->can_throw_non_call_exceptions)
{
if (side_effects_p (op1))
return simplify_gen_binary (AND, mode, op1, trueop0);
/* Convert divide by power of two into shift. */
if (CONST_INT_P (trueop1)
&& (val = exact_log2 (UINTVAL (trueop1))) > 0)
- return simplify_gen_binary (LSHIFTRT, mode, op0, GEN_INT (val));
+ return simplify_gen_binary (LSHIFTRT, mode, op0,
+ gen_int_shift_amount (mode, val));
break;
case DIV:
#if defined(HAVE_rotate) && defined(HAVE_rotatert)
if (CONST_INT_P (trueop1)
&& IN_RANGE (INTVAL (trueop1),
- GET_MODE_PRECISION (mode) / 2 + (code == ROTATE),
- GET_MODE_PRECISION (mode) - 1))
- return simplify_gen_binary (code == ROTATE ? ROTATERT : ROTATE,
- mode, op0, GEN_INT (GET_MODE_PRECISION (mode)
- - INTVAL (trueop1)));
+ GET_MODE_UNIT_PRECISION (mode) / 2 + (code == ROTATE),
+ GET_MODE_UNIT_PRECISION (mode) - 1))
+ {
+ int new_amount = GET_MODE_UNIT_PRECISION (mode) - INTVAL (trueop1);
+ rtx new_amount_rtx = gen_int_shift_amount (mode, new_amount);
+ return simplify_gen_binary (code == ROTATE ? ROTATERT : ROTATE,
+ mode, op0, new_amount_rtx);
+ }
#endif
/* FALLTHRU */
case ASHIFTRT:
if (trueop0 == CONST0_RTX (mode) && ! side_effects_p (op1))
return op0;
/* Rotating ~0 always results in ~0. */
- if (CONST_INT_P (trueop0) && width <= HOST_BITS_PER_WIDE_INT
+ if (CONST_INT_P (trueop0)
+ && HWI_COMPUTABLE_MODE_P (mode)
&& UINTVAL (trueop0) == GET_MODE_MASK (mode)
&& ! side_effects_p (op1))
return op0;
+
+ canonicalize_shift:
/* Given:
scalar modes M1, M2
scalar constants c1, c2
size (M2) > size (M1)
c1 == size (M2) - size (M1)
optimize:
- (ashiftrt:M1 (subreg:M1 (lshiftrt:M2 (reg:M2) (const_int <c1>))
+ ([a|l]shiftrt:M1 (subreg:M1 (lshiftrt:M2 (reg:M2) (const_int <c1>))
<low_part>)
(const_int <c2>))
to:
- (subreg:M1 (ashiftrt:M2 (reg:M2) (const_int <c1 + c2>))
+ (subreg:M1 ([a|l]shiftrt:M2 (reg:M2) (const_int <c1 + c2>))
<low_part>). */
- if (code == ASHIFTRT
- && !VECTOR_MODE_P (mode)
+ if ((code == ASHIFTRT || code == LSHIFTRT)
+ && is_a <scalar_int_mode> (mode, &int_mode)
&& SUBREG_P (op0)
&& CONST_INT_P (op1)
&& GET_CODE (SUBREG_REG (op0)) == LSHIFTRT
- && !VECTOR_MODE_P (GET_MODE (SUBREG_REG (op0)))
+ && is_a <scalar_int_mode> (GET_MODE (SUBREG_REG (op0)),
+ &inner_mode)
&& CONST_INT_P (XEXP (SUBREG_REG (op0), 1))
- && (GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (op0)))
- > GET_MODE_BITSIZE (mode))
+ && GET_MODE_BITSIZE (inner_mode) > GET_MODE_BITSIZE (int_mode)
&& (INTVAL (XEXP (SUBREG_REG (op0), 1))
- == (GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (op0)))
- - GET_MODE_BITSIZE (mode)))
+ == GET_MODE_BITSIZE (inner_mode) - GET_MODE_BITSIZE (int_mode))
&& subreg_lowpart_p (op0))
{
- rtx tmp = GEN_INT (INTVAL (XEXP (SUBREG_REG (op0), 1))
- + INTVAL (op1));
- machine_mode inner_mode = GET_MODE (SUBREG_REG (op0));
- tmp = simplify_gen_binary (ASHIFTRT,
- GET_MODE (SUBREG_REG (op0)),
+ rtx tmp = gen_int_shift_amount
+ (inner_mode, INTVAL (XEXP (SUBREG_REG (op0), 1)) + INTVAL (op1));
+ tmp = simplify_gen_binary (code, inner_mode,
XEXP (SUBREG_REG (op0), 0),
tmp);
- return lowpart_subreg (mode, tmp, inner_mode);
+ return lowpart_subreg (int_mode, tmp, inner_mode);
}
- canonicalize_shift:
+
if (SHIFT_COUNT_TRUNCATED && CONST_INT_P (op1))
{
- val = INTVAL (op1) & (GET_MODE_PRECISION (mode) - 1);
+ val = INTVAL (op1) & (GET_MODE_UNIT_PRECISION (mode) - 1);
if (val != INTVAL (op1))
- return simplify_gen_binary (code, mode, op0, GEN_INT (val));
+ return simplify_gen_binary (code, mode, op0,
+ gen_int_shift_amount (mode, val));
}
break;
return op0;
/* Optimize (lshiftrt (clz X) C) as (eq X 0). */
if (GET_CODE (op0) == CLZ
+ && is_a <scalar_int_mode> (GET_MODE (XEXP (op0, 0)), &inner_mode)
&& CONST_INT_P (trueop1)
&& STORE_FLAG_VALUE == 1
- && INTVAL (trueop1) < (HOST_WIDE_INT)width)
+ && INTVAL (trueop1) < GET_MODE_UNIT_PRECISION (mode))
{
- machine_mode imode = GET_MODE (XEXP (op0, 0));
unsigned HOST_WIDE_INT zero_val = 0;
- if (CLZ_DEFINED_VALUE_AT_ZERO (imode, zero_val)
- && zero_val == GET_MODE_PRECISION (imode)
+ if (CLZ_DEFINED_VALUE_AT_ZERO (inner_mode, zero_val)
+ && zero_val == GET_MODE_PRECISION (inner_mode)
&& INTVAL (trueop1) == exact_log2 (zero_val))
- return simplify_gen_relational (EQ, mode, imode,
+ return simplify_gen_relational (EQ, mode, inner_mode,
XEXP (op0, 0), const0_rtx);
}
goto canonicalize_shift;
case SMIN:
- if (width <= HOST_BITS_PER_WIDE_INT
+ if (HWI_COMPUTABLE_MODE_P (mode)
&& mode_signbit_p (mode, trueop1)
&& ! side_effects_p (op0))
return op1;
break;
case SMAX:
- if (width <= HOST_BITS_PER_WIDE_INT
+ if (HWI_COMPUTABLE_MODE_P (mode)
&& CONST_INT_P (trueop1)
&& (UINTVAL (trueop1) == GET_MODE_MASK (mode) >> 1)
&& ! side_effects_p (op0))
/* ??? There are simplifications that can be done. */
return 0;
+ case VEC_SERIES:
+ if (op1 == CONST0_RTX (GET_MODE_INNER (mode)))
+ return gen_vec_duplicate (mode, op0);
+ if (CONSTANT_P (op0) && CONSTANT_P (op1))
+ return gen_const_vec_series (mode, op0, op1);
+ return 0;
+
case VEC_SELECT:
if (!VECTOR_MODE_P (mode))
{
gcc_assert (XVECLEN (trueop1, 0) == 1);
gcc_assert (CONST_INT_P (XVECEXP (trueop1, 0, 0)));
+ if (vec_duplicate_p (trueop0, &elt0))
+ return elt0;
+
if (GET_CODE (trueop0) == CONST_VECTOR)
return CONST_VECTOR_ELT (trueop0, INTVAL (XVECEXP
(trueop1, 0, 0)));
rtx op0 = XEXP (trueop0, 0);
rtx op1 = XEXP (trueop0, 1);
- machine_mode opmode = GET_MODE (op0);
- int elt_size = GET_MODE_UNIT_SIZE (opmode);
- int n_elts = GET_MODE_SIZE (opmode) / elt_size;
+ int n_elts = GET_MODE_NUNITS (GET_MODE (op0));
int i = INTVAL (XVECEXP (trueop1, 0, 0));
int elem;
mode01 = GET_MODE (op01);
/* Find out number of elements of each operand. */
- if (VECTOR_MODE_P (mode00))
- {
- elt_size = GET_MODE_UNIT_SIZE (mode00);
- n_elts00 = GET_MODE_SIZE (mode00) / elt_size;
- }
- else
- n_elts00 = 1;
-
- if (VECTOR_MODE_P (mode01))
- {
- elt_size = GET_MODE_UNIT_SIZE (mode01);
- n_elts01 = GET_MODE_SIZE (mode01) / elt_size;
- }
- else
- n_elts01 = 1;
+ n_elts00 = GET_MODE_NUNITS (mode00);
+ n_elts01 = GET_MODE_NUNITS (mode01);
gcc_assert (n_elts == n_elts00 + n_elts01);
tmp_op, gen_rtx_PARALLEL (VOIDmode, vec));
return tmp;
}
- if (GET_CODE (trueop0) == VEC_DUPLICATE
- && GET_MODE (XEXP (trueop0, 0)) == mode)
- return XEXP (trueop0, 0);
}
else
{
== GET_MODE_INNER (GET_MODE (trueop0)));
gcc_assert (GET_CODE (trueop1) == PARALLEL);
+ if (vec_duplicate_p (trueop0, &elt0))
+ /* It doesn't matter which elements are selected by trueop1,
+ because they are all the same. */
+ return gen_vec_duplicate (mode, elt0);
+
if (GET_CODE (trueop0) == CONST_VECTOR)
{
int elt_size = GET_MODE_UNIT_SIZE (mode);
rtx subop1 = XEXP (trueop0, 1);
machine_mode mode0 = GET_MODE (subop0);
machine_mode mode1 = GET_MODE (subop1);
- int li = GET_MODE_UNIT_SIZE (mode0);
- int l0 = GET_MODE_SIZE (mode0) / li;
- int l1 = GET_MODE_SIZE (mode1) / li;
+ int l0 = GET_MODE_NUNITS (mode0);
+ int l1 = GET_MODE_NUNITS (mode1);
int i0 = INTVAL (XVECEXP (trueop1, 0, 0));
if (i0 == 0 && !side_effects_p (op1) && mode == mode0)
{
for (int i = 0; i < XVECLEN (trueop1, 0); i++)
{
rtx j = XVECEXP (trueop1, 0, i);
- if (sel & (1 << UINTVAL (j)))
+ if (sel & (HOST_WIDE_INT_1U << UINTVAL (j)))
all_operand1 = false;
else
all_operand0 = false;
|| CONST_SCALAR_INT_P (trueop1)
|| CONST_DOUBLE_AS_FLOAT_P (trueop1)))
{
- int elt_size = GET_MODE_UNIT_SIZE (mode);
- unsigned n_elts = (GET_MODE_SIZE (mode) / elt_size);
+ unsigned n_elts = GET_MODE_NUNITS (mode);
+ unsigned in_n_elts = GET_MODE_NUNITS (op0_mode);
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)
gcc_unreachable ();
}
+ if (mode == GET_MODE (op0)
+ && mode == GET_MODE (op1)
+ && vec_duplicate_p (op0, &elt0)
+ && vec_duplicate_p (op1, &elt1))
+ {
+ /* Try applying the operator to ELT and see if that simplifies.
+ We can duplicate the result if so.
+
+ The reason we don't use simplify_gen_binary is that it isn't
+ necessarily a win to convert things like:
+
+ (plus:V (vec_duplicate:V (reg:S R1))
+ (vec_duplicate:V (reg:S R2)))
+
+ to:
+
+ (vec_duplicate:V (plus:S (reg:S R1) (reg:S R2)))
+
+ The first might be done entirely in vector registers while the
+ second might need a move between register files. */
+ tem = simplify_binary_operation (code, GET_MODE_INNER (mode),
+ elt0, elt1);
+ if (tem)
+ return gen_vec_duplicate (mode, tem);
+ }
+
return 0;
}
simplify_const_binary_operation (enum rtx_code code, machine_mode mode,
rtx op0, rtx op1)
{
- unsigned int width = GET_MODE_PRECISION (mode);
-
if (VECTOR_MODE_P (mode)
&& code != VEC_CONCAT
&& GET_CODE (op0) == CONST_VECTOR
&& GET_CODE (op1) == CONST_VECTOR)
{
- unsigned n_elts = GET_MODE_NUNITS (mode);
- machine_mode op0mode = GET_MODE (op0);
- unsigned op0_n_elts = GET_MODE_NUNITS (op0mode);
- machine_mode op1mode = GET_MODE (op1);
- unsigned op1_n_elts = GET_MODE_NUNITS (op1mode);
+ unsigned int n_elts = CONST_VECTOR_NUNITS (op0);
+ gcc_assert (n_elts == (unsigned int) CONST_VECTOR_NUNITS (op1));
+ gcc_assert (n_elts == GET_MODE_NUNITS (mode));
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),
else
{
REAL_VALUE_TYPE f0, f1, value, result;
+ const REAL_VALUE_TYPE *opr0, *opr1;
bool inexact;
- real_convert (&f0, mode, CONST_DOUBLE_REAL_VALUE (op0));
- real_convert (&f1, mode, CONST_DOUBLE_REAL_VALUE (op1));
+ opr0 = CONST_DOUBLE_REAL_VALUE (op0);
+ opr1 = CONST_DOUBLE_REAL_VALUE (op1);
if (HONOR_SNANS (mode)
- && (REAL_VALUE_ISNAN (f0) || REAL_VALUE_ISNAN (f1)))
+ && (REAL_VALUE_ISSIGNALING_NAN (*opr0)
+ || REAL_VALUE_ISSIGNALING_NAN (*opr1)))
return 0;
+ real_convert (&f0, mode, opr0);
+ real_convert (&f1, mode, opr1);
+
if (code == DIV
&& real_equal (&f1, &dconst0)
&& (flag_trapping_math || ! MODE_HAS_INFINITIES (mode)))
}
/* We can fold some multi-word operations. */
- if ((GET_MODE_CLASS (mode) == MODE_INT
- || GET_MODE_CLASS (mode) == MODE_PARTIAL_INT)
+ scalar_int_mode int_mode;
+ if (is_a <scalar_int_mode> (mode, &int_mode)
&& CONST_SCALAR_INT_P (op0)
&& CONST_SCALAR_INT_P (op1))
{
wide_int result;
bool overflow;
- rtx_mode_t pop0 = std::make_pair (op0, mode);
- rtx_mode_t pop1 = std::make_pair (op1, mode);
+ rtx_mode_t pop0 = rtx_mode_t (op0, int_mode);
+ rtx_mode_t pop1 = rtx_mode_t (op1, int_mode);
#if TARGET_SUPPORTS_WIDE_INT == 0
/* This assert keeps the simplification from producing a result
simplify something and so you if you added this to the test
above the code would die later anyway. If this assert
happens, you just need to make the port support wide int. */
- gcc_assert (width <= HOST_BITS_PER_DOUBLE_INT);
+ gcc_assert (GET_MODE_PRECISION (int_mode) <= HOST_BITS_PER_DOUBLE_INT);
#endif
switch (code)
{
{
wide_int wop1 = pop1;
if (SHIFT_COUNT_TRUNCATED)
- wop1 = wi::umod_trunc (wop1, width);
- else if (wi::geu_p (wop1, width))
+ wop1 = wi::umod_trunc (wop1, GET_MODE_PRECISION (int_mode));
+ else if (wi::geu_p (wop1, GET_MODE_PRECISION (int_mode)))
return NULL_RTX;
switch (code)
default:
return NULL_RTX;
}
- return immed_wide_int_const (result, mode);
+ return immed_wide_int_const (result, int_mode);
+ }
+
+ /* Handle polynomial integers. */
+ if (NUM_POLY_INT_COEFFS > 1
+ && is_a <scalar_int_mode> (mode, &int_mode)
+ && poly_int_rtx_p (op0)
+ && poly_int_rtx_p (op1))
+ {
+ poly_wide_int result;
+ switch (code)
+ {
+ case PLUS:
+ result = wi::to_poly_wide (op0, mode) + wi::to_poly_wide (op1, mode);
+ break;
+
+ case MINUS:
+ result = wi::to_poly_wide (op0, mode) - wi::to_poly_wide (op1, mode);
+ break;
+
+ case MULT:
+ if (CONST_SCALAR_INT_P (op1))
+ result = wi::to_poly_wide (op0, mode) * rtx_mode_t (op1, mode);
+ else
+ return NULL_RTX;
+ break;
+
+ case ASHIFT:
+ if (CONST_SCALAR_INT_P (op1))
+ {
+ wide_int shift = rtx_mode_t (op1, mode);
+ if (SHIFT_COUNT_TRUNCATED)
+ shift = wi::umod_trunc (shift, GET_MODE_PRECISION (int_mode));
+ else if (wi::geu_p (shift, GET_MODE_PRECISION (int_mode)))
+ return NULL_RTX;
+ result = wi::to_poly_wide (op0, mode) << shift;
+ }
+ else
+ return NULL_RTX;
+ break;
+
+ case IOR:
+ if (!CONST_SCALAR_INT_P (op1)
+ || !can_ior_p (wi::to_poly_wide (op0, mode),
+ rtx_mode_t (op1, mode), &result))
+ return NULL_RTX;
+ break;
+
+ default:
+ return NULL_RTX;
+ }
+ return immed_wide_int_const (result, int_mode);
}
return NULL_RTX;
n_ops = i;
}
- /* If nothing changed, fail. */
+ /* If nothing changed, check that rematerialization of rtl instructions
+ is still required. */
if (!canonicalized)
- return NULL_RTX;
+ {
+ /* Perform rematerialization if only all operands are registers and
+ all operations are PLUS. */
+ /* ??? Also disallow (non-global, non-frame) fixed registers to work
+ around rs6000 and how it uses the CA register. See PR67145. */
+ for (i = 0; i < n_ops; i++)
+ if (ops[i].neg
+ || !REG_P (ops[i].op)
+ || (REGNO (ops[i].op) < FIRST_PSEUDO_REGISTER
+ && fixed_regs[REGNO (ops[i].op)]
+ && !global_regs[REGNO (ops[i].op)]
+ && ops[i].op != frame_pointer_rtx
+ && ops[i].op != arg_pointer_rtx
+ && ops[i].op != stack_pointer_rtx))
+ return NULL_RTX;
+ goto gen_result;
+ }
/* Create (minus -C X) instead of (neg (const (plus X C))). */
if (n_ops == 2
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 (mode, ops[n_ops - 2].op,
- INTVAL (value));
- n_ops--;
+ if (CONST_INT_P (value))
+ {
+ ops[n_ops - 2].op = plus_constant (mode, ops[n_ops - 2].op,
+ INTVAL (value));
+ n_ops--;
+ }
}
/* Put a non-negated operand first, if possible. */
}
/* Now make the result by performing the requested operations. */
+ gen_result:
result = ops[0].op;
for (i = 1; i < n_ops; i++)
result = gen_rtx_fmt_ee (ops[i].neg ? MINUS : PLUS,
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);
+ return gen_const_vec_duplicate (mode, val);
}
#else
return NULL_RTX;
}
else if (code == EQ)
{
- enum rtx_code new_code = reversed_comparison_code (op0, NULL_RTX);
+ enum rtx_code new_code = reversed_comparison_code (op0, NULL);
if (new_code != UNKNOWN)
return simplify_gen_relational (new_code, mode, VOIDmode,
XEXP (op0, 0), XEXP (op0, 1));
cmp_mode, XEXP (op0, 0), new_cmp);
}
+ /* (GTU (PLUS a C) (C - 1)) where C is a non-zero constant can be
+ transformed into (LTU a -C). */
+ if (code == GTU && GET_CODE (op0) == PLUS && CONST_INT_P (op1)
+ && CONST_INT_P (XEXP (op0, 1))
+ && (UINTVAL (op1) == UINTVAL (XEXP (op0, 1)) - 1)
+ && XEXP (op0, 1) != const0_rtx)
+ {
+ rtx new_cmp
+ = simplify_gen_unary (NEG, cmp_mode, XEXP (op0, 1), cmp_mode);
+ return simplify_gen_relational (LTU, mode, cmp_mode,
+ XEXP (op0, 0), new_cmp);
+ }
+
/* Canonicalize (LTU/GEU (PLUS a b) b) as (LTU/GEU (PLUS a b) a). */
if ((code == LTU || code == GEU)
&& GET_CODE (op0) == PLUS
/* (ne:SI (zero_extract:SI FOO (const_int 1) BAR) (const_int 0))) is
the same as (zero_extract:SI FOO (const_int 1) BAR). */
+ scalar_int_mode int_mode, int_cmp_mode;
if (code == NE
&& op1 == const0_rtx
- && GET_MODE_CLASS (mode) == MODE_INT
- && cmp_mode != VOIDmode
+ && is_int_mode (mode, &int_mode)
+ && is_a <scalar_int_mode> (cmp_mode, &int_cmp_mode)
/* ??? Work-around BImode bugs in the ia64 backend. */
- && mode != BImode
- && cmp_mode != BImode
- && nonzero_bits (op0, cmp_mode) == 1
+ && int_mode != BImode
+ && int_cmp_mode != BImode
+ && nonzero_bits (op0, int_cmp_mode) == 1
&& STORE_FLAG_VALUE == 1)
- return GET_MODE_SIZE (mode) > GET_MODE_SIZE (cmp_mode)
- ? simplify_gen_unary (ZERO_EXTEND, mode, op0, cmp_mode)
- : lowpart_subreg (mode, op0, cmp_mode);
+ return GET_MODE_SIZE (int_mode) > GET_MODE_SIZE (int_cmp_mode)
+ ? simplify_gen_unary (ZERO_EXTEND, int_mode, op0, int_cmp_mode)
+ : lowpart_subreg (int_mode, op0, int_cmp_mode);
/* (eq/ne (xor x y) 0) simplifies to (eq/ne x y). */
if ((code == EQ || code == NE)
&& (code == EQ || code == NE)
&& ! ((REG_P (op0) || CONST_INT_P (trueop0))
&& (REG_P (op1) || CONST_INT_P (trueop1)))
- && 0 != (tem = simplify_binary_operation (MINUS, mode, op0, op1))
+ && (tem = simplify_binary_operation (MINUS, mode, op0, op1)) != 0
/* We cannot do this if tem is a nonzero address. */
&& ! nonzero_address_p (tem))
return simplify_const_relational_operation (signed_condition (code),
largest int representable on the target is as good as
infinite. */
machine_mode cmode = (mode == VOIDmode) ? MAX_MODE_INT : mode;
- rtx_mode_t ptrueop0 = std::make_pair (trueop0, cmode);
- rtx_mode_t ptrueop1 = std::make_pair (trueop1, cmode);
+ rtx_mode_t ptrueop0 = rtx_mode_t (trueop0, cmode);
+ rtx_mode_t ptrueop1 = rtx_mode_t (trueop1, cmode);
if (wi::eq_p (ptrueop0, ptrueop1))
return comparison_result (code, CMP_EQ);
}
/* Optimize comparisons with upper and lower bounds. */
- if (HWI_COMPUTABLE_MODE_P (mode)
- && CONST_INT_P (trueop1)
+ scalar_int_mode int_mode;
+ if (CONST_INT_P (trueop1)
+ && is_a <scalar_int_mode> (mode, &int_mode)
+ && HWI_COMPUTABLE_MODE_P (int_mode)
&& !side_effects_p (trueop0))
{
int sign;
- unsigned HOST_WIDE_INT nonzero = nonzero_bits (trueop0, mode);
+ unsigned HOST_WIDE_INT nonzero = nonzero_bits (trueop0, int_mode);
HOST_WIDE_INT val = INTVAL (trueop1);
HOST_WIDE_INT mmin, mmax;
sign = 1;
/* Get a reduced range if the sign bit is zero. */
- if (nonzero <= (GET_MODE_MASK (mode) >> 1))
+ if (nonzero <= (GET_MODE_MASK (int_mode) >> 1))
{
mmin = 0;
mmax = nonzero;
else
{
rtx mmin_rtx, mmax_rtx;
- get_mode_bounds (mode, sign, mode, &mmin_rtx, &mmax_rtx);
+ get_mode_bounds (int_mode, sign, int_mode, &mmin_rtx, &mmax_rtx);
mmin = INTVAL (mmin_rtx);
mmax = INTVAL (mmax_rtx);
if (sign)
{
- unsigned int sign_copies = num_sign_bit_copies (trueop0, mode);
+ unsigned int sign_copies
+ = num_sign_bit_copies (trueop0, int_mode);
mmin >>= (sign_copies - 1);
mmax >>= (sign_copies - 1);
}
/* Optimize integer comparisons with zero. */
- if (trueop1 == const0_rtx && !side_effects_p (trueop0))
+ if (is_a <scalar_int_mode> (mode, &int_mode)
+ && trueop1 == const0_rtx
+ && !side_effects_p (trueop0))
{
/* Some addresses are known to be nonzero. We don't know
their sign, but equality comparisons are known. */
rtx inner_const = avoid_constant_pool_reference (XEXP (op0, 1));
if (CONST_INT_P (inner_const) && inner_const != const0_rtx)
{
- int sign_bitnum = GET_MODE_PRECISION (mode) - 1;
+ int sign_bitnum = GET_MODE_PRECISION (int_mode) - 1;
int has_sign = (HOST_BITS_PER_WIDE_INT >= sign_bitnum
&& (UINTVAL (inner_const)
- & ((unsigned HOST_WIDE_INT) 1
+ & (HOST_WIDE_INT_1U
<< sign_bitnum)));
switch (code)
{
case LT:
/* Optimize abs(x) < 0.0. */
- if (!HONOR_SNANS (mode)
- && (!INTEGRAL_MODE_P (mode)
- || (!flag_wrapv && !flag_trapv && flag_strict_overflow)))
- {
- if (INTEGRAL_MODE_P (mode)
- && (issue_strict_overflow_warning
- (WARN_STRICT_OVERFLOW_CONDITIONAL)))
- warning (OPT_Wstrict_overflow,
- ("assuming signed overflow does not occur when "
- "assuming abs (x) < 0 is false"));
- return const0_rtx;
- }
+ if (!INTEGRAL_MODE_P (mode) && !HONOR_SNANS (mode))
+ return const0_rtx;
break;
case GE:
/* Optimize abs(x) >= 0.0. */
- if (!HONOR_NANS (mode)
- && (!INTEGRAL_MODE_P (mode)
- || (!flag_wrapv && !flag_trapv && flag_strict_overflow)))
- {
- if (INTEGRAL_MODE_P (mode)
- && (issue_strict_overflow_warning
- (WARN_STRICT_OVERFLOW_CONDITIONAL)))
- warning (OPT_Wstrict_overflow,
- ("assuming signed overflow does not occur when "
- "assuming abs (x) >= 0 is true"));
- return const_true_rtx;
- }
+ if (!INTEGRAL_MODE_P (mode) && !HONOR_NANS (mode))
+ return const_true_rtx;
break;
case UNGE:
return 0;
}
+
+/* Recognize expressions of the form (X CMP 0) ? VAL : OP (X)
+ where OP is CLZ or CTZ and VAL is the value from CLZ_DEFINED_VALUE_AT_ZERO
+ or CTZ_DEFINED_VALUE_AT_ZERO respectively and return OP (X) if the expression
+ can be simplified to that or NULL_RTX if not.
+ Assume X is compared against zero with CMP_CODE and the true
+ arm is TRUE_VAL and the false arm is FALSE_VAL. */
+
+static rtx
+simplify_cond_clz_ctz (rtx x, rtx_code cmp_code, rtx true_val, rtx false_val)
+{
+ if (cmp_code != EQ && cmp_code != NE)
+ return NULL_RTX;
+
+ /* Result on X == 0 and X !=0 respectively. */
+ rtx on_zero, on_nonzero;
+ if (cmp_code == EQ)
+ {
+ on_zero = true_val;
+ on_nonzero = false_val;
+ }
+ else
+ {
+ on_zero = false_val;
+ on_nonzero = true_val;
+ }
+
+ rtx_code op_code = GET_CODE (on_nonzero);
+ if ((op_code != CLZ && op_code != CTZ)
+ || !rtx_equal_p (XEXP (on_nonzero, 0), x)
+ || !CONST_INT_P (on_zero))
+ return NULL_RTX;
+
+ HOST_WIDE_INT op_val;
+ scalar_int_mode mode ATTRIBUTE_UNUSED
+ = as_a <scalar_int_mode> (GET_MODE (XEXP (on_nonzero, 0)));
+ if (((op_code == CLZ && CLZ_DEFINED_VALUE_AT_ZERO (mode, op_val))
+ || (op_code == CTZ && CTZ_DEFINED_VALUE_AT_ZERO (mode, op_val)))
+ && op_val == INTVAL (on_zero))
+ return on_nonzero;
+
+ return NULL_RTX;
+}
+
\f
/* Simplify CODE, an operation with result mode MODE and three operands,
OP0, OP1, and OP2. OP0_MODE was the mode of OP0 before it became
machine_mode op0_mode, rtx op0, rtx op1,
rtx op2)
{
- unsigned int width = GET_MODE_PRECISION (mode);
bool any_change = false;
rtx tem, trueop2;
-
- /* VOIDmode means "infinite" precision. */
- if (width == 0)
- width = HOST_BITS_PER_WIDE_INT;
+ scalar_int_mode int_mode, int_op0_mode;
switch (code)
{
if (CONST_INT_P (op0)
&& CONST_INT_P (op1)
&& CONST_INT_P (op2)
- && ((unsigned) INTVAL (op1) + (unsigned) INTVAL (op2) <= width)
- && width <= (unsigned) HOST_BITS_PER_WIDE_INT)
+ && is_a <scalar_int_mode> (mode, &int_mode)
+ && INTVAL (op1) + INTVAL (op2) <= GET_MODE_PRECISION (int_mode)
+ && HWI_COMPUTABLE_MODE_P (int_mode))
{
/* Extracting a bit-field from a constant */
unsigned HOST_WIDE_INT val = UINTVAL (op0);
HOST_WIDE_INT op1val = INTVAL (op1);
HOST_WIDE_INT op2val = INTVAL (op2);
- if (BITS_BIG_ENDIAN)
- val >>= GET_MODE_PRECISION (op0_mode) - op2val - op1val;
- else
+ if (!BITS_BIG_ENDIAN)
val >>= op2val;
+ else if (is_a <scalar_int_mode> (op0_mode, &int_op0_mode))
+ val >>= GET_MODE_PRECISION (int_op0_mode) - op2val - op1val;
+ else
+ /* Not enough information to calculate the bit position. */
+ break;
if (HOST_BITS_PER_WIDE_INT != op1val)
{
/* First zero-extend. */
- val &= ((unsigned HOST_WIDE_INT) 1 << op1val) - 1;
+ val &= (HOST_WIDE_INT_1U << op1val) - 1;
/* If desired, propagate sign bit. */
if (code == SIGN_EXTRACT
- && (val & ((unsigned HOST_WIDE_INT) 1 << (op1val - 1)))
+ && (val & (HOST_WIDE_INT_1U << (op1val - 1)))
!= 0)
- val |= ~ (((unsigned HOST_WIDE_INT) 1 << op1val) - 1);
+ val |= ~ ((HOST_WIDE_INT_1U << op1val) - 1);
}
- return gen_int_mode (val, mode);
+ return gen_int_mode (val, int_mode);
}
break;
}
}
+ /* Convert x == 0 ? N : clz (x) into clz (x) when
+ CLZ_DEFINED_VALUE_AT_ZERO is defined to N for the mode of x.
+ Similarly for ctz (x). */
+ if (COMPARISON_P (op0) && !side_effects_p (op0)
+ && XEXP (op0, 1) == const0_rtx)
+ {
+ rtx simplified
+ = simplify_cond_clz_ctz (XEXP (op0, 0), GET_CODE (op0),
+ op1, op2);
+ if (simplified)
+ return simplified;
+ }
+
if (COMPARISON_P (op0) && ! side_effects_p (op0))
{
machine_mode cmp_mode = (GET_MODE (XEXP (op0, 0)) == VOIDmode
else if (t == 0 && f == STORE_FLAG_VALUE)
{
enum rtx_code tmp;
- tmp = reversed_comparison_code (op0, NULL_RTX);
+ tmp = reversed_comparison_code (op0, NULL);
if (tmp == UNKNOWN)
break;
code = tmp;
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));
trueop2 = avoid_constant_pool_reference (op2);
if (CONST_INT_P (trueop2))
{
- int elt_size = GET_MODE_UNIT_SIZE (mode);
- unsigned n_elts = (GET_MODE_SIZE (mode) / elt_size);
+ unsigned n_elts = GET_MODE_NUNITS (mode);
unsigned HOST_WIDE_INT sel = UINTVAL (trueop2);
unsigned HOST_WIDE_INT mask;
if (n_elts == HOST_BITS_PER_WIDE_INT)
mask = -1;
else
- mask = ((unsigned HOST_WIDE_INT) 1 << n_elts) - 1;
+ mask = (HOST_WIDE_INT_1U << n_elts) - 1;
if (!(sel & mask) && !side_effects_p (op0))
return op1;
unsigned int i;
for (i = 0; i < n_elts; i++)
- RTVEC_ELT (v, i) = ((sel & ((unsigned HOST_WIDE_INT) 1 << i))
+ RTVEC_ELT (v, i) = ((sel & (HOST_WIDE_INT_1U << i))
? CONST_VECTOR_ELT (trueop0, i)
: CONST_VECTOR_ELT (trueop1, i));
return gen_rtx_CONST_VECTOR (mode, v);
return op1;
}
}
+ /* Replace (vec_merge (vec_duplicate (X)) (const_vector [A, B])
+ (const_int N))
+ with (vec_concat (X) (B)) if N == 1 or
+ (vec_concat (A) (X)) if N == 2. */
+ if (GET_CODE (op0) == VEC_DUPLICATE
+ && GET_CODE (op1) == CONST_VECTOR
+ && CONST_VECTOR_NUNITS (op1) == 2
+ && GET_MODE_NUNITS (GET_MODE (op0)) == 2
+ && IN_RANGE (sel, 1, 2))
+ {
+ rtx newop0 = XEXP (op0, 0);
+ rtx newop1 = CONST_VECTOR_ELT (op1, 2 - sel);
+ if (sel == 2)
+ std::swap (newop0, newop1);
+ return simplify_gen_binary (VEC_CONCAT, mode, newop0, newop1);
+ }
+ /* Replace (vec_merge (vec_duplicate x) (vec_concat (y) (z)) (const_int N))
+ with (vec_concat x z) if N == 1, or (vec_concat y x) if N == 2.
+ Only applies for vectors of two elements. */
+ if (GET_CODE (op0) == VEC_DUPLICATE
+ && GET_CODE (op1) == VEC_CONCAT
+ && GET_MODE_NUNITS (GET_MODE (op0)) == 2
+ && GET_MODE_NUNITS (GET_MODE (op1)) == 2
+ && IN_RANGE (sel, 1, 2))
+ {
+ rtx newop0 = XEXP (op0, 0);
+ rtx newop1 = XEXP (op1, 2 - sel);
+ rtx otherop = XEXP (op1, sel - 1);
+ if (sel == 2)
+ std::swap (newop0, newop1);
+ /* Don't want to throw away the other part of the vec_concat if
+ it has side-effects. */
+ if (!side_effects_p (otherop))
+ return simplify_gen_binary (VEC_CONCAT, mode, newop0, newop1);
+ }
+
+ /* Replace (vec_merge (vec_duplicate x) (vec_duplicate y)
+ (const_int n))
+ with (vec_concat x y) or (vec_concat y x) depending on value
+ of N. */
+ if (GET_CODE (op0) == VEC_DUPLICATE
+ && GET_CODE (op1) == VEC_DUPLICATE
+ && GET_MODE_NUNITS (GET_MODE (op0)) == 2
+ && GET_MODE_NUNITS (GET_MODE (op1)) == 2
+ && IN_RANGE (sel, 1, 2))
+ {
+ rtx newop0 = XEXP (op0, 0);
+ rtx newop1 = XEXP (op1, 0);
+ if (sel == 2)
+ std::swap (newop0, newop1);
+
+ return simplify_gen_binary (VEC_CONCAT, mode, newop0, newop1);
+ }
}
if (rtx_equal_p (op0, op1)
and then repacking them again for OUTERMODE. */
static rtx
-simplify_immed_subreg (machine_mode outermode, rtx op,
- machine_mode innermode, unsigned int byte)
+simplify_immed_subreg (fixed_size_mode outermode, rtx op,
+ fixed_size_mode innermode, unsigned int byte)
{
enum {
value_bit = 8,
int num_elem;
rtx * elems;
int elem_bitsize;
- rtx result_s;
+ rtx result_s = NULL;
rtvec result_v = NULL;
enum mode_class outer_class;
- machine_mode outer_submode;
+ scalar_mode outer_submode;
int max_bitsize;
/* Some ports misuse CCmode. */
case CONST_WIDE_INT:
{
- rtx_mode_t val = std::make_pair (el, innermode);
+ rtx_mode_t val = rtx_mode_t (el, GET_MODE_INNER (innermode));
unsigned char extend = wi::sign_mask (val);
+ int prec = wi::get_precision (val);
- for (i = 0; i < elem_bitsize; i += value_bit)
+ for (i = 0; i < prec && i < elem_bitsize; i += value_bit)
*vp++ = wi::extract_uhwi (val, i, value_bit);
for (; i < elem_bitsize; i += value_bit)
*vp++ = extend;
{
/* This is big enough for anything on the platform. */
long tmp[MAX_BITSIZE_MODE_ANY_MODE / 32];
- int bitsize = GET_MODE_BITSIZE (GET_MODE (el));
+ scalar_float_mode el_mode;
+
+ el_mode = as_a <scalar_float_mode> (GET_MODE (el));
+ int bitsize = GET_MODE_BITSIZE (el_mode);
- gcc_assert (SCALAR_FLOAT_MODE_P (GET_MODE (el)));
gcc_assert (bitsize <= elem_bitsize);
gcc_assert (bitsize % value_bit == 0);
case MODE_DECIMAL_FLOAT:
{
REAL_VALUE_TYPE r;
- long tmp[MAX_BITSIZE_MODE_ANY_MODE / 32];
+ long tmp[MAX_BITSIZE_MODE_ANY_MODE / 32] = { 0 };
/* real_from_target wants its input in words affected by
FLOAT_WORDS_BIG_ENDIAN. However, we ignore this,
and use WORDS_BIG_ENDIAN instead; see the documentation
of SUBREG in rtl.texi. */
- for (i = 0; i < max_bitsize / 32; i++)
- tmp[i] = 0;
for (i = 0; i < elem_bitsize; i += value_bit)
{
int ibase;
Return 0 if no simplifications are possible. */
rtx
simplify_subreg (machine_mode outermode, rtx op,
- machine_mode innermode, unsigned int byte)
+ machine_mode innermode, poly_uint64 byte)
{
/* Little bit of sanity checking. */
gcc_assert (innermode != VOIDmode);
gcc_assert (GET_MODE (op) == innermode
|| GET_MODE (op) == VOIDmode);
- if ((byte % GET_MODE_SIZE (outermode)) != 0)
+ if (!multiple_p (byte, GET_MODE_SIZE (outermode)))
return NULL_RTX;
- if (byte >= GET_MODE_SIZE (innermode))
+ if (maybe_ge (byte, GET_MODE_SIZE (innermode)))
return NULL_RTX;
- if (outermode == innermode && !byte)
+ if (outermode == innermode && known_eq (byte, 0U))
return op;
+ if (multiple_p (byte, GET_MODE_UNIT_SIZE (innermode)))
+ {
+ rtx elt;
+
+ if (VECTOR_MODE_P (outermode)
+ && GET_MODE_INNER (outermode) == GET_MODE_INNER (innermode)
+ && vec_duplicate_p (op, &elt))
+ return gen_vec_duplicate (outermode, elt);
+
+ if (outermode == GET_MODE_INNER (innermode)
+ && vec_duplicate_p (op, &elt))
+ return elt;
+ }
+
if (CONST_SCALAR_INT_P (op)
|| CONST_DOUBLE_AS_FLOAT_P (op)
|| GET_CODE (op) == CONST_FIXED
|| GET_CODE (op) == CONST_VECTOR)
- return simplify_immed_subreg (outermode, op, innermode, byte);
+ {
+ /* simplify_immed_subreg deconstructs OP into bytes and constructs
+ the result from bytes, so it only works if the sizes of the modes
+ and the value of the offset are known at compile time. Cases that
+ that apply to general modes and offsets should be handled here
+ before calling simplify_immed_subreg. */
+ fixed_size_mode fs_outermode, fs_innermode;
+ unsigned HOST_WIDE_INT cbyte;
+ if (is_a <fixed_size_mode> (outermode, &fs_outermode)
+ && is_a <fixed_size_mode> (innermode, &fs_innermode)
+ && byte.is_constant (&cbyte))
+ return simplify_immed_subreg (fs_outermode, op, fs_innermode, cbyte);
+
+ return NULL_RTX;
+ }
/* Changing mode twice with SUBREG => just change it once,
or not at all if changing back op starting mode. */
if (GET_CODE (op) == SUBREG)
{
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)
+ && known_eq (byte, 0U)
+ && known_eq (SUBREG_BYTE (op), 0))
return SUBREG_REG (op);
- /* The SUBREG_BYTE represents offset, as if the value were stored
- in memory. Irritating exception is paradoxical subreg, where
- we define SUBREG_BYTE to be 0. On big endian machines, this
- value should be negative. For a moment, undo this exception. */
- 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;
- }
+ /* Work out the memory offset of the final OUTERMODE value relative
+ to the inner value of OP. */
+ poly_int64 mem_offset = subreg_memory_offset (outermode,
+ innermode, byte);
+ poly_int64 op_mem_offset = subreg_memory_offset (op);
+ poly_int64 final_offset = mem_offset + op_mem_offset;
/* See whether resulting subreg will be paradoxical. */
- if (GET_MODE_SIZE (innermostmode) > GET_MODE_SIZE (outermode))
+ if (!paradoxical_subreg_p (outermode, innermostmode))
{
/* In nonparadoxical subregs we can't handle negative offsets. */
- if (final_offset < 0)
+ if (maybe_lt (final_offset, 0))
return NULL_RTX;
/* Bail out in case resulting subreg would be incorrect. */
- if (final_offset % GET_MODE_SIZE (outermode)
- || (unsigned) final_offset >= GET_MODE_SIZE (innermostmode))
+ if (!multiple_p (final_offset, GET_MODE_SIZE (outermode))
+ || maybe_ge (final_offset, GET_MODE_SIZE (innermostmode)))
return NULL_RTX;
}
else
{
- int offset = 0;
- int difference = (GET_MODE_SIZE (innermostmode) - GET_MODE_SIZE (outermode));
-
- /* In paradoxical subreg, see if we are still looking on lower part.
- If so, our SUBREG_BYTE will be 0. */
- 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
+ poly_int64 required_offset = subreg_memory_offset (outermode,
+ innermostmode, 0);
+ if (maybe_ne (final_offset, required_offset))
return NULL_RTX;
+ /* Paradoxical subregs always have byte offset 0. */
+ final_offset = 0;
}
/* Recurse for further possible simplifications. */
final_regno = simplify_subreg_regno (regno, innermode, byte, outermode);
if (HARD_REGISTER_NUM_P (final_regno))
{
- rtx x;
- int final_offset = byte;
-
- /* Adjust offset for paradoxical subregs. */
- 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;
- }
-
- x = gen_rtx_REG_offset (op, outermode, final_regno, final_offset);
+ rtx x = gen_rtx_REG_offset (op, outermode, final_regno,
+ subreg_memory_offset (outermode,
+ innermode, byte));
/* Propagate original regno. We don't have any way to specify
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. */
- if (subreg_lowpart_offset (outermode, innermode) == byte)
+ if (known_eq (subreg_lowpart_offset (outermode, innermode), byte))
ORIGINAL_REGNO (x) = ORIGINAL_REGNO (op);
return x;
}
&& GET_MODE_SIZE (outermode) <= GET_MODE_SIZE (GET_MODE (op)))
return adjust_address_nv (op, outermode, byte);
- /* Handle complex values represented as CONCAT
- of real and imaginary part. */
- if (GET_CODE (op) == CONCAT)
+ /* Handle complex or vector values represented as CONCAT or VEC_CONCAT
+ of two parts. */
+ if (GET_CODE (op) == CONCAT
+ || GET_CODE (op) == VEC_CONCAT)
{
- unsigned int part_size, final_offset;
+ unsigned int part_size;
+ poly_uint64 final_offset;
rtx part, res;
- part_size = GET_MODE_UNIT_SIZE (GET_MODE (XEXP (op, 0)));
- if (byte < part_size)
+ machine_mode part_mode = GET_MODE (XEXP (op, 0));
+ if (part_mode == VOIDmode)
+ part_mode = GET_MODE_INNER (GET_MODE (op));
+ part_size = GET_MODE_SIZE (part_mode);
+ if (known_lt (byte, part_size))
{
part = XEXP (op, 0);
final_offset = byte;
}
- else
+ else if (known_ge (byte, part_size))
{
part = XEXP (op, 1);
final_offset = byte - part_size;
}
+ else
+ return NULL_RTX;
- if (final_offset + GET_MODE_SIZE (outermode) > part_size)
+ if (maybe_gt (final_offset + GET_MODE_SIZE (outermode), part_size))
return NULL_RTX;
- res = simplify_subreg (outermode, part, GET_MODE (part), final_offset);
+ part_mode = GET_MODE (part);
+ if (part_mode == VOIDmode)
+ part_mode = GET_MODE_INNER (GET_MODE (op));
+ res = simplify_subreg (outermode, part, part_mode, final_offset);
if (res)
return res;
- if (validate_subreg (outermode, GET_MODE (part), part, final_offset))
+ if (validate_subreg (outermode, part_mode, part, final_offset))
return gen_rtx_SUBREG (outermode, part, final_offset);
return NULL_RTX;
}
it extracts higher bits that the ZERO_EXTEND's source bits. */
if (GET_CODE (op) == ZERO_EXTEND && SCALAR_INT_MODE_P (innermode))
{
- unsigned int bitpos = subreg_lsb_1 (outermode, innermode, byte);
- if (bitpos >= GET_MODE_PRECISION (GET_MODE (XEXP (op, 0))))
+ poly_uint64 bitpos = subreg_lsb_1 (outermode, innermode, byte);
+ if (known_ge (bitpos, GET_MODE_PRECISION (GET_MODE (XEXP (op, 0)))))
return CONST0_RTX (outermode);
}
- if (SCALAR_INT_MODE_P (outermode)
- && SCALAR_INT_MODE_P (innermode)
- && GET_MODE_PRECISION (outermode) < GET_MODE_PRECISION (innermode)
- && byte == subreg_lowpart_offset (outermode, innermode))
+ scalar_int_mode int_outermode, int_innermode;
+ if (is_a <scalar_int_mode> (outermode, &int_outermode)
+ && is_a <scalar_int_mode> (innermode, &int_innermode)
+ && known_eq (byte, subreg_lowpart_offset (int_outermode, int_innermode)))
{
- rtx tem = simplify_truncation (outermode, op, innermode);
- if (tem)
- return tem;
+ /* Handle polynomial integers. The upper bits of a paradoxical
+ subreg are undefined, so this is safe regardless of whether
+ we're truncating or extending. */
+ if (CONST_POLY_INT_P (op))
+ {
+ poly_wide_int val
+ = poly_wide_int::from (const_poly_int_value (op),
+ GET_MODE_PRECISION (int_outermode),
+ SIGNED);
+ return immed_wide_int_const (val, int_outermode);
+ }
+
+ if (GET_MODE_PRECISION (int_outermode)
+ < GET_MODE_PRECISION (int_innermode))
+ {
+ rtx tem = simplify_truncation (int_outermode, op, int_innermode);
+ if (tem)
+ return tem;
+ }
}
return NULL_RTX;
rtx
simplify_gen_subreg (machine_mode outermode, rtx op,
- machine_mode innermode, unsigned int byte)
+ machine_mode innermode, poly_uint64 byte)
{
rtx newx;
if (swap_commutative_operands_p (XEXP (x, 0), XEXP (x, 1)))
return simplify_gen_binary (code, mode, XEXP (x, 1), XEXP (x, 0));
- /* Fall through.... */
+ /* Fall through. */
case RTX_BIN_ARITH:
return simplify_binary_operation (code, mode, XEXP (x, 0), XEXP (x, 1));
}
return NULL;
}
+
+#if CHECKING_P
+
+namespace selftest {
+
+/* Make a unique pseudo REG of mode MODE for use by selftests. */
+
+static rtx
+make_test_reg (machine_mode mode)
+{
+ static int test_reg_num = LAST_VIRTUAL_REGISTER + 1;
+
+ return gen_rtx_REG (mode, test_reg_num++);
+}
+
+/* Test vector simplifications involving VEC_DUPLICATE in which the
+ operands and result have vector mode MODE. SCALAR_REG is a pseudo
+ register that holds one element of MODE. */
+
+static void
+test_vector_ops_duplicate (machine_mode mode, rtx scalar_reg)
+{
+ scalar_mode inner_mode = GET_MODE_INNER (mode);
+ rtx duplicate = gen_rtx_VEC_DUPLICATE (mode, scalar_reg);
+ unsigned int nunits = GET_MODE_NUNITS (mode);
+ if (GET_MODE_CLASS (mode) == MODE_VECTOR_INT)
+ {
+ /* Test some simple unary cases with VEC_DUPLICATE arguments. */
+ rtx not_scalar_reg = gen_rtx_NOT (inner_mode, scalar_reg);
+ rtx duplicate_not = gen_rtx_VEC_DUPLICATE (mode, not_scalar_reg);
+ ASSERT_RTX_EQ (duplicate,
+ simplify_unary_operation (NOT, mode,
+ duplicate_not, mode));
+
+ rtx neg_scalar_reg = gen_rtx_NEG (inner_mode, scalar_reg);
+ rtx duplicate_neg = gen_rtx_VEC_DUPLICATE (mode, neg_scalar_reg);
+ ASSERT_RTX_EQ (duplicate,
+ simplify_unary_operation (NEG, mode,
+ duplicate_neg, mode));
+
+ /* Test some simple binary cases with VEC_DUPLICATE arguments. */
+ ASSERT_RTX_EQ (duplicate,
+ simplify_binary_operation (PLUS, mode, duplicate,
+ CONST0_RTX (mode)));
+
+ ASSERT_RTX_EQ (duplicate,
+ simplify_binary_operation (MINUS, mode, duplicate,
+ CONST0_RTX (mode)));
+
+ ASSERT_RTX_PTR_EQ (CONST0_RTX (mode),
+ simplify_binary_operation (MINUS, mode, duplicate,
+ duplicate));
+ }
+
+ /* Test a scalar VEC_SELECT of a VEC_DUPLICATE. */
+ rtx zero_par = gen_rtx_PARALLEL (VOIDmode, gen_rtvec (1, const0_rtx));
+ ASSERT_RTX_PTR_EQ (scalar_reg,
+ simplify_binary_operation (VEC_SELECT, inner_mode,
+ duplicate, zero_par));
+
+ /* And again with the final element. */
+ rtx last_index = gen_int_mode (GET_MODE_NUNITS (mode) - 1, word_mode);
+ rtx last_par = gen_rtx_PARALLEL (VOIDmode, gen_rtvec (1, last_index));
+ ASSERT_RTX_PTR_EQ (scalar_reg,
+ simplify_binary_operation (VEC_SELECT, inner_mode,
+ duplicate, last_par));
+
+ /* Test a scalar subreg of a VEC_DUPLICATE. */
+ poly_uint64 offset = subreg_lowpart_offset (inner_mode, mode);
+ ASSERT_RTX_EQ (scalar_reg,
+ simplify_gen_subreg (inner_mode, duplicate,
+ mode, offset));
+
+ machine_mode narrower_mode;
+ if (nunits > 2
+ && mode_for_vector (inner_mode, 2).exists (&narrower_mode)
+ && VECTOR_MODE_P (narrower_mode))
+ {
+ /* Test VEC_SELECT of a vector. */
+ rtx vec_par
+ = gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, const1_rtx, const0_rtx));
+ rtx narrower_duplicate
+ = gen_rtx_VEC_DUPLICATE (narrower_mode, scalar_reg);
+ ASSERT_RTX_EQ (narrower_duplicate,
+ simplify_binary_operation (VEC_SELECT, narrower_mode,
+ duplicate, vec_par));
+
+ /* Test a vector subreg of a VEC_DUPLICATE. */
+ poly_uint64 offset = subreg_lowpart_offset (narrower_mode, mode);
+ ASSERT_RTX_EQ (narrower_duplicate,
+ simplify_gen_subreg (narrower_mode, duplicate,
+ mode, offset));
+ }
+}
+
+/* Test vector simplifications involving VEC_SERIES in which the
+ operands and result have vector mode MODE. SCALAR_REG is a pseudo
+ register that holds one element of MODE. */
+
+static void
+test_vector_ops_series (machine_mode mode, rtx scalar_reg)
+{
+ /* Test unary cases with VEC_SERIES arguments. */
+ scalar_mode inner_mode = GET_MODE_INNER (mode);
+ rtx duplicate = gen_rtx_VEC_DUPLICATE (mode, scalar_reg);
+ rtx neg_scalar_reg = gen_rtx_NEG (inner_mode, scalar_reg);
+ rtx series_0_r = gen_rtx_VEC_SERIES (mode, const0_rtx, scalar_reg);
+ rtx series_0_nr = gen_rtx_VEC_SERIES (mode, const0_rtx, neg_scalar_reg);
+ rtx series_nr_1 = gen_rtx_VEC_SERIES (mode, neg_scalar_reg, const1_rtx);
+ rtx series_r_m1 = gen_rtx_VEC_SERIES (mode, scalar_reg, constm1_rtx);
+ rtx series_r_r = gen_rtx_VEC_SERIES (mode, scalar_reg, scalar_reg);
+ rtx series_nr_nr = gen_rtx_VEC_SERIES (mode, neg_scalar_reg,
+ neg_scalar_reg);
+ ASSERT_RTX_EQ (series_0_r,
+ simplify_unary_operation (NEG, mode, series_0_nr, mode));
+ ASSERT_RTX_EQ (series_r_m1,
+ simplify_unary_operation (NEG, mode, series_nr_1, mode));
+ ASSERT_RTX_EQ (series_r_r,
+ simplify_unary_operation (NEG, mode, series_nr_nr, mode));
+
+ /* Test that a VEC_SERIES with a zero step is simplified away. */
+ ASSERT_RTX_EQ (duplicate,
+ simplify_binary_operation (VEC_SERIES, mode,
+ scalar_reg, const0_rtx));
+
+ /* Test PLUS and MINUS with VEC_SERIES. */
+ rtx series_0_1 = gen_const_vec_series (mode, const0_rtx, const1_rtx);
+ rtx series_0_m1 = gen_const_vec_series (mode, const0_rtx, constm1_rtx);
+ rtx series_r_1 = gen_rtx_VEC_SERIES (mode, scalar_reg, const1_rtx);
+ ASSERT_RTX_EQ (series_r_r,
+ simplify_binary_operation (PLUS, mode, series_0_r,
+ duplicate));
+ ASSERT_RTX_EQ (series_r_1,
+ simplify_binary_operation (PLUS, mode, duplicate,
+ series_0_1));
+ ASSERT_RTX_EQ (series_r_m1,
+ simplify_binary_operation (PLUS, mode, duplicate,
+ series_0_m1));
+ ASSERT_RTX_EQ (series_0_r,
+ simplify_binary_operation (MINUS, mode, series_r_r,
+ duplicate));
+ ASSERT_RTX_EQ (series_r_m1,
+ simplify_binary_operation (MINUS, mode, duplicate,
+ series_0_1));
+ ASSERT_RTX_EQ (series_r_1,
+ simplify_binary_operation (MINUS, mode, duplicate,
+ series_0_m1));
+ ASSERT_RTX_EQ (series_0_m1,
+ simplify_binary_operation (VEC_SERIES, mode, const0_rtx,
+ constm1_rtx));
+}
+
+/* Verify some simplifications involving vectors. */
+
+static void
+test_vector_ops ()
+{
+ for (unsigned int i = 0; i < NUM_MACHINE_MODES; ++i)
+ {
+ machine_mode mode = (machine_mode) i;
+ if (VECTOR_MODE_P (mode))
+ {
+ rtx scalar_reg = make_test_reg (GET_MODE_INNER (mode));
+ test_vector_ops_duplicate (mode, scalar_reg);
+ if (GET_MODE_CLASS (mode) == MODE_VECTOR_INT
+ && GET_MODE_NUNITS (mode) > 2)
+ test_vector_ops_series (mode, scalar_reg);
+ }
+ }
+}
+
+template<unsigned int N>
+struct simplify_const_poly_int_tests
+{
+ static void run ();
+};
+
+template<>
+struct simplify_const_poly_int_tests<1>
+{
+ static void run () {}
+};
+
+/* Test various CONST_POLY_INT properties. */
+
+template<unsigned int N>
+void
+simplify_const_poly_int_tests<N>::run ()
+{
+ rtx x1 = gen_int_mode (poly_int64 (1, 1), QImode);
+ rtx x2 = gen_int_mode (poly_int64 (-80, 127), QImode);
+ rtx x3 = gen_int_mode (poly_int64 (-79, -128), QImode);
+ rtx x4 = gen_int_mode (poly_int64 (5, 4), QImode);
+ rtx x5 = gen_int_mode (poly_int64 (30, 24), QImode);
+ rtx x6 = gen_int_mode (poly_int64 (20, 16), QImode);
+ rtx x7 = gen_int_mode (poly_int64 (7, 4), QImode);
+ rtx x8 = gen_int_mode (poly_int64 (30, 24), HImode);
+ rtx x9 = gen_int_mode (poly_int64 (-30, -24), HImode);
+ rtx x10 = gen_int_mode (poly_int64 (-31, -24), HImode);
+ rtx two = GEN_INT (2);
+ rtx six = GEN_INT (6);
+ poly_uint64 offset = subreg_lowpart_offset (QImode, HImode);
+
+ /* These tests only try limited operation combinations. Fuller arithmetic
+ testing is done directly on poly_ints. */
+ ASSERT_EQ (simplify_unary_operation (NEG, HImode, x8, HImode), x9);
+ ASSERT_EQ (simplify_unary_operation (NOT, HImode, x8, HImode), x10);
+ ASSERT_EQ (simplify_unary_operation (TRUNCATE, QImode, x8, HImode), x5);
+ ASSERT_EQ (simplify_binary_operation (PLUS, QImode, x1, x2), x3);
+ ASSERT_EQ (simplify_binary_operation (MINUS, QImode, x3, x1), x2);
+ ASSERT_EQ (simplify_binary_operation (MULT, QImode, x4, six), x5);
+ ASSERT_EQ (simplify_binary_operation (MULT, QImode, six, x4), x5);
+ ASSERT_EQ (simplify_binary_operation (ASHIFT, QImode, x4, two), x6);
+ ASSERT_EQ (simplify_binary_operation (IOR, QImode, x4, two), x7);
+ ASSERT_EQ (simplify_subreg (HImode, x5, QImode, 0), x8);
+ ASSERT_EQ (simplify_subreg (QImode, x8, HImode, offset), x5);
+}
+
+/* Run all of the selftests within this file. */
+
+void
+simplify_rtx_c_tests ()
+{
+ test_vector_ops ();
+ simplify_const_poly_int_tests<NUM_POLY_INT_COEFFS>::run ();
+}
+
+} // namespace selftest
+
+#endif /* CHECKING_P */