summaryrefslogtreecommitdiff
path: root/gcc/recog.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/recog.c')
-rw-r--r--gcc/recog.c150
1 files changed, 99 insertions, 51 deletions
diff --git a/gcc/recog.c b/gcc/recog.c
index 1596a019067..e278f985529 100644
--- a/gcc/recog.c
+++ b/gcc/recog.c
@@ -517,13 +517,15 @@ validate_replace_rtx_1 (loc, from, to, object)
if (GET_CODE (XEXP (x, 0)) == SUBREG)
{
if (GET_MODE_SIZE (GET_MODE (XEXP (x, 0))) <= UNITS_PER_WORD)
- to = operand_subword (to, SUBREG_WORD (XEXP (x, 0)),
+ to = operand_subword (to,
+ (SUBREG_BYTE (XEXP (x, 0))
+ / UNITS_PER_WORD),
0, GET_MODE (from));
else if (GET_MODE_CLASS (GET_MODE (from)) == MODE_INT
&& (GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0)))
<= HOST_BITS_PER_WIDE_INT))
{
- int i = SUBREG_WORD (XEXP (x, 0)) * BITS_PER_WORD;
+ int i = SUBREG_BYTE (XEXP (x, 0)) * BITS_PER_UNIT;
HOST_WIDE_INT valh;
unsigned HOST_WIDE_INT vall;
@@ -569,26 +571,21 @@ validate_replace_rtx_1 (loc, from, to, object)
break;
case SUBREG:
- /* In case we are replacing by constant, attempt to simplify it to non-SUBREG
- expression. We can't do this later, since the information about inner mode
- may be lost. */
+ /* In case we are replacing by constant, attempt to simplify it to
+ non-SUBREG expression. We can't do this later, since the information
+ about inner mode may be lost. */
if (CONSTANT_P (to) && rtx_equal_p (SUBREG_REG (x), from))
{
- if (GET_MODE_SIZE (GET_MODE (x)) == UNITS_PER_WORD
- && GET_MODE_SIZE (GET_MODE (from)) > UNITS_PER_WORD
- && GET_MODE_CLASS (GET_MODE (x)) == MODE_INT)
- {
- rtx temp = operand_subword (to, SUBREG_WORD (x),
- 0, GET_MODE (from));
- if (temp)
- {
- validate_change (object, loc, temp, 1);
- return;
- }
- }
- if (subreg_lowpart_p (x))
+ int offset, part;
+ unsigned HOST_WIDE_INT val;
+
+ /* A paradoxical SUBREG of a VOIDmode constant is the same constant,
+ since we are saying that the high bits don't matter. */
+ if (GET_MODE (to) == VOIDmode
+ && (GET_MODE_SIZE (GET_MODE (x))
+ >= GET_MODE_SIZE (GET_MODE (from))))
{
- rtx new = gen_lowpart_if_possible (GET_MODE (x), to);
+ rtx new = gen_lowpart_if_possible (GET_MODE (x), to);
if (new)
{
validate_change (object, loc, new, 1);
@@ -596,13 +593,67 @@ validate_replace_rtx_1 (loc, from, to, object)
}
}
- /* A paradoxical SUBREG of a VOIDmode constant is the same constant,
- since we are saying that the high bits don't matter. */
- if (GET_MODE (to) == VOIDmode
- && GET_MODE_SIZE (GET_MODE (x)) > GET_MODE_SIZE (GET_MODE (from)))
+ offset = SUBREG_BYTE (x) * BITS_PER_UNIT;
+ switch (GET_CODE (to))
{
- validate_change (object, loc, to, 1);
- return;
+ case CONST_DOUBLE:
+ if (GET_MODE (to) != VOIDmode)
+ break;
+
+ part = offset >= HOST_BITS_PER_WIDE_INT;
+ if ((BITS_PER_WORD > HOST_BITS_PER_WIDE_INT
+ && BYTES_BIG_ENDIAN)
+ || (BITS_PER_WORD <= HOST_BITS_PER_WIDE_INT
+ && WORDS_BIG_ENDIAN))
+ part = !part;
+ val = part ? CONST_DOUBLE_HIGH (to) : CONST_DOUBLE_LOW (to);
+ offset %= HOST_BITS_PER_WIDE_INT;
+
+ /* FALLTHROUGH */
+ case CONST_INT:
+ if (GET_CODE (to) == CONST_INT)
+ val = INTVAL (to);
+
+ {
+ /* Avoid creating bogus SUBREGs */
+ enum machine_mode mode = GET_MODE (x);
+ enum machine_mode inner_mode = GET_MODE (from);
+
+ /* We've already picked the word we want from a double, so
+ pretend this is actually an integer. */
+ if (GET_CODE (to) == CONST_DOUBLE)
+ inner_mode = SImode;
+
+ if (GET_MODE_CLASS (mode) != MODE_INT)
+ abort ();
+
+ if (BYTES_BIG_ENDIAN || WORDS_BIG_ENDIAN)
+ {
+ if (WORDS_BIG_ENDIAN)
+ offset = GET_MODE_BITSIZE (inner_mode)
+ - GET_MODE_BITSIZE (mode) - offset;
+ if (BYTES_BIG_ENDIAN != WORDS_BIG_ENDIAN
+ && GET_MODE_SIZE (mode) < UNITS_PER_WORD)
+ offset = offset + BITS_PER_WORD - GET_MODE_BITSIZE (mode)
+ - 2 * (offset % BITS_PER_WORD);
+ }
+
+ if (offset >= HOST_BITS_PER_WIDE_INT)
+ to = ((HOST_WIDE_INT) val < 0) ? constm1_rtx : const0_rtx;
+ else
+ {
+ val >>= offset;
+ if (GET_MODE_BITSIZE (mode) < HOST_BITS_PER_WIDE_INT)
+ val = trunc_int_for_mode (val, mode);
+ to = GEN_INT (val);
+ }
+
+ validate_change (object, loc, to, 1);
+ return;
+ }
+
+ default:
+ break;
}
}
@@ -612,15 +663,26 @@ validate_replace_rtx_1 (loc, from, to, object)
&& rtx_equal_p (SUBREG_REG (x), from))
{
if (GET_MODE (x) == GET_MODE (SUBREG_REG (to))
- && SUBREG_WORD (x) == 0 && SUBREG_WORD (to) == 0)
+ && SUBREG_BYTE (x) == 0 && SUBREG_BYTE (to) == 0)
{
validate_change (object, loc, SUBREG_REG (to), 1);
return;
}
- validate_change (object, loc,
- gen_rtx_SUBREG (GET_MODE (x), SUBREG_REG (to),
- SUBREG_WORD (x) + SUBREG_WORD (to)), 1);
+ /* Make sure the 2 byte counts added together are an even unit
+ of x's mode, and combine them if so. Otherwise we run
+ into problems with something like:
+ (subreg:HI (subreg:QI (SI:55) 3) 0)
+ we end up with an odd offset into a HI which is invalid. */
+
+ if (SUBREG_BYTE (to) % GET_MODE_SIZE (GET_MODE (x)) == 0)
+ validate_change (object, loc,
+ gen_rtx_SUBREG (GET_MODE (x), SUBREG_REG (to),
+ SUBREG_BYTE(x) + SUBREG_BYTE (to)),
+ 1);
+ else
+ validate_change (object, loc, to, 1);
+
return;
}
@@ -636,15 +698,10 @@ validate_replace_rtx_1 (loc, from, to, object)
&& ! MEM_VOLATILE_P (to)
&& GET_MODE_SIZE (GET_MODE (x)) <= GET_MODE_SIZE (GET_MODE (to)))
{
- int offset = SUBREG_WORD (x) * UNITS_PER_WORD;
+ int offset = SUBREG_BYTE (x);
enum machine_mode mode = GET_MODE (x);
rtx new;
- if (BYTES_BIG_ENDIAN)
- offset += (MIN (UNITS_PER_WORD,
- GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))))
- - MIN (UNITS_PER_WORD, GET_MODE_SIZE (mode)));
-
new = gen_rtx_MEM (mode, plus_constant (XEXP (to, 0), offset));
MEM_COPY_ATTRIBUTES (new, to);
validate_change (object, loc, new, 1);
@@ -694,8 +751,8 @@ validate_replace_rtx_1 (loc, from, to, object)
int offset = pos / BITS_PER_UNIT;
rtx newmem;
- /* If the bytes and bits are counted differently, we
- must adjust the offset. */
+ /* If the bytes and bits are counted differently, we
+ must adjust the offset. */
if (BYTES_BIG_ENDIAN != BITS_BIG_ENDIAN)
offset = (GET_MODE_SIZE (is_mode) - GET_MODE_SIZE (wanted_mode)
- offset);
@@ -1040,7 +1097,6 @@ general_operand (op, mode)
enum machine_mode mode;
{
register enum rtx_code code = GET_CODE (op);
- int mode_altering_drug = 0;
if (mode == VOIDmode)
mode = GET_MODE (op);
@@ -1078,11 +1134,6 @@ general_operand (op, mode)
op = SUBREG_REG (op);
code = GET_CODE (op);
-#if 0
- /* No longer needed, since (SUBREG (MEM...))
- will load the MEM into a reload reg in the MEM's own mode. */
- mode_altering_drug = 1;
-#endif
}
if (code == REG)
@@ -1113,8 +1164,6 @@ general_operand (op, mode)
return 0;
win:
- if (mode_altering_drug)
- return ! mode_dependent_address_p (XEXP (op, 0));
return 1;
}
@@ -1467,13 +1516,9 @@ indirect_operand (op, mode)
if (! reload_completed
&& GET_CODE (op) == SUBREG && GET_CODE (SUBREG_REG (op)) == MEM)
{
- register int offset = SUBREG_WORD (op) * UNITS_PER_WORD;
+ register int offset = SUBREG_BYTE (op);
rtx inner = SUBREG_REG (op);
- if (BYTES_BIG_ENDIAN)
- offset -= (MIN (UNITS_PER_WORD, GET_MODE_SIZE (GET_MODE (op)))
- - MIN (UNITS_PER_WORD, GET_MODE_SIZE (GET_MODE (inner))));
-
if (mode != VOIDmode && GET_MODE (op) != mode)
return 0;
@@ -2482,7 +2527,10 @@ constrain_operands (strict)
{
if (GET_CODE (SUBREG_REG (op)) == REG
&& REGNO (SUBREG_REG (op)) < FIRST_PSEUDO_REGISTER)
- offset = SUBREG_WORD (op);
+ offset = subreg_regno_offset (REGNO (SUBREG_REG (op)),
+ GET_MODE (SUBREG_REG (op)),
+ SUBREG_BYTE (op),
+ GET_MODE (op));
op = SUBREG_REG (op);
}