gen_lowpart_common (enum machine_mode mode, rtx x)
{
int msize = GET_MODE_SIZE (mode);
- int xsize = GET_MODE_SIZE (GET_MODE (x));
+ int xsize;
int offset = 0;
+ enum machine_mode innermode;
+
+ /* Unfortunately, this routine doesn't take a parameter for the mode of X,
+ so we have to make one up. Yuk. */
+ innermode = GET_MODE (x);
+ if (GET_CODE (x) == CONST_INT && msize <= HOST_BITS_PER_WIDE_INT)
+ innermode = mode_for_size (HOST_BITS_PER_WIDE_INT, MODE_INT, 0);
+ else if (innermode == VOIDmode)
+ innermode = mode_for_size (HOST_BITS_PER_WIDE_INT * 2, MODE_INT, 0);
+
+ xsize = GET_MODE_SIZE (innermode);
+
+ if (innermode == VOIDmode || innermode == BLKmode)
+ abort ();
- if (GET_MODE (x) == mode)
+ if (innermode == mode)
return x;
/* MODE must occupy no more words than the mode of X. */
- if (GET_MODE (x) != VOIDmode
- && ((msize + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD
- > ((xsize + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD)))
+ if ((msize + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD
+ > ((xsize + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD))
return 0;
/* Don't allow generating paradoxical FLOAT_MODE subregs. */
- if (GET_MODE_CLASS (mode) == MODE_FLOAT
- && GET_MODE (x) != VOIDmode && msize > xsize)
+ if (GET_MODE_CLASS (mode) == MODE_FLOAT && msize > xsize)
return 0;
- offset = subreg_lowpart_offset (mode, GET_MODE (x));
+ offset = subreg_lowpart_offset (mode, innermode);
if ((GET_CODE (x) == ZERO_EXTEND || GET_CODE (x) == SIGN_EXTEND)
&& (GET_MODE_CLASS (mode) == MODE_INT
if (GET_MODE (XEXP (x, 0)) == mode)
return XEXP (x, 0);
- else if (GET_MODE_SIZE (mode) < GET_MODE_SIZE (GET_MODE (XEXP (x, 0))))
+ else if (msize < GET_MODE_SIZE (GET_MODE (XEXP (x, 0))))
return gen_lowpart_common (mode, XEXP (x, 0));
- else if (GET_MODE_SIZE (mode) < GET_MODE_SIZE (GET_MODE (x)))
+ else if (msize < xsize)
return gen_rtx_fmt_e (GET_CODE (x), mode, XEXP (x, 0));
}
else if (GET_CODE (x) == SUBREG || GET_CODE (x) == REG
- || GET_CODE (x) == CONCAT || GET_CODE (x) == CONST_VECTOR)
- return simplify_gen_subreg (mode, x, GET_MODE (x), offset);
- else if (VECTOR_MODE_P (mode) && GET_MODE (x) == VOIDmode)
- return simplify_gen_subreg (mode, x, int_mode_for_mode (mode), offset);
- /* If X is a CONST_INT or a CONST_DOUBLE, extract the appropriate bits
- from the low-order part of the constant. */
- else if ((GET_MODE_CLASS (mode) == MODE_INT
- || GET_MODE_CLASS (mode) == MODE_PARTIAL_INT)
- && GET_MODE (x) == VOIDmode
- && (GET_CODE (x) == CONST_INT || GET_CODE (x) == CONST_DOUBLE))
- {
- /* If MODE is twice the host word size, X is already the desired
- representation. Otherwise, if MODE is wider than a word, we can't
- do this. If MODE is exactly a word, return just one CONST_INT. */
-
- if (GET_MODE_BITSIZE (mode) >= 2 * HOST_BITS_PER_WIDE_INT)
- return x;
- else if (GET_MODE_BITSIZE (mode) > HOST_BITS_PER_WIDE_INT)
- return 0;
- else if (GET_MODE_BITSIZE (mode) == HOST_BITS_PER_WIDE_INT)
- return (GET_CODE (x) == CONST_INT ? x
- : GEN_INT (CONST_DOUBLE_LOW (x)));
- else
- {
- /* MODE must be narrower than HOST_BITS_PER_WIDE_INT. */
- HOST_WIDE_INT val = (GET_CODE (x) == CONST_INT ? INTVAL (x)
- : CONST_DOUBLE_LOW (x));
-
- /* Sign extend to HOST_WIDE_INT. */
- val = trunc_int_for_mode (val, mode);
-
- return (GET_CODE (x) == CONST_INT && INTVAL (x) == val ? x
- : GEN_INT (val));
- }
- }
-
- /* The floating-point emulator can handle all conversions between
- FP and integer operands. This simplifies reload because it
- doesn't have to deal with constructs like (subreg:DI
- (const_double:SF ...)) or (subreg:DF (const_int ...)). */
- /* Single-precision floats are always 32-bits and double-precision
- floats are always 64-bits. */
-
- else if (GET_MODE_CLASS (mode) == MODE_FLOAT
- && GET_MODE_BITSIZE (mode) == 32
- && GET_CODE (x) == CONST_INT)
- {
- REAL_VALUE_TYPE r;
- long i = INTVAL (x);
-
- real_from_target (&r, &i, mode);
- return CONST_DOUBLE_FROM_REAL_VALUE (r, mode);
- }
- else if (GET_MODE_CLASS (mode) == MODE_FLOAT
- && GET_MODE_BITSIZE (mode) == 64
- && (GET_CODE (x) == CONST_INT || GET_CODE (x) == CONST_DOUBLE)
- && GET_MODE (x) == VOIDmode)
- {
- REAL_VALUE_TYPE r;
- HOST_WIDE_INT low, high;
- long i[2];
-
- if (GET_CODE (x) == CONST_INT)
- {
- low = INTVAL (x);
- high = low >> (HOST_BITS_PER_WIDE_INT - 1);
- }
- else
- {
- low = CONST_DOUBLE_LOW (x);
- high = CONST_DOUBLE_HIGH (x);
- }
-
- if (HOST_BITS_PER_WIDE_INT > 32)
- high = low >> 31 >> 1;
-
- /* REAL_VALUE_TARGET_DOUBLE takes the addressing order of the
- target machine. */
- if (WORDS_BIG_ENDIAN)
- i[0] = high, i[1] = low;
- else
- i[0] = low, i[1] = high;
-
- real_from_target (&r, i, mode);
- return CONST_DOUBLE_FROM_REAL_VALUE (r, mode);
- }
- else if ((GET_MODE_CLASS (mode) == MODE_INT
- || GET_MODE_CLASS (mode) == MODE_PARTIAL_INT)
- && GET_CODE (x) == CONST_DOUBLE
- && GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
- {
- REAL_VALUE_TYPE r;
- long i[4]; /* Only the low 32 bits of each 'long' are used. */
- int endian = WORDS_BIG_ENDIAN ? 1 : 0;
-
- /* Convert 'r' into an array of four 32-bit words in target word
- order. */
- REAL_VALUE_FROM_CONST_DOUBLE (r, x);
- switch (GET_MODE_BITSIZE (GET_MODE (x)))
- {
- case 32:
- REAL_VALUE_TO_TARGET_SINGLE (r, i[3 * endian]);
- i[1] = 0;
- i[2] = 0;
- i[3 - 3 * endian] = 0;
- break;
- case 64:
- REAL_VALUE_TO_TARGET_DOUBLE (r, i + 2 * endian);
- i[2 - 2 * endian] = 0;
- i[3 - 2 * endian] = 0;
- break;
- case 96:
- REAL_VALUE_TO_TARGET_LONG_DOUBLE (r, i + endian);
- i[3 - 3 * endian] = 0;
- break;
- case 128:
- REAL_VALUE_TO_TARGET_LONG_DOUBLE (r, i);
- break;
- default:
- abort ();
- }
- /* Now, pack the 32-bit elements of the array into a CONST_DOUBLE
- and return it. */
-#if HOST_BITS_PER_WIDE_INT == 32
- return immed_double_const (i[3 * endian], i[1 + endian], mode);
-#else
- if (HOST_BITS_PER_WIDE_INT != 64)
- abort ();
-
- return immed_double_const ((((unsigned long) i[3 * endian])
- | ((HOST_WIDE_INT) i[1 + endian] << 32)),
- (((unsigned long) i[2 - endian])
- | ((HOST_WIDE_INT) i[3 - 3 * endian] << 32)),
- mode);
-#endif
- }
- /* If MODE is a condition code and X is a CONST_INT, the value of X
- must already have been "recognized" by the back-end, and we can
- assume that it is valid for this mode. */
- else if (GET_MODE_CLASS (mode) == MODE_CC
- && GET_CODE (x) == CONST_INT)
- return x;
+ || GET_CODE (x) == CONCAT || GET_CODE (x) == CONST_VECTOR
+ || GET_CODE (x) == CONST_DOUBLE || GET_CODE (x) == CONST_INT)
+ return simplify_gen_subreg (mode, x, innermode, offset);
/* Otherwise, we can't do this. */
return 0;
== SUBREG_BYTE (x));
}
\f
-
-/* Helper routine for all the constant cases of operand_subword.
- Some places invoke this directly. */
-
-rtx
-constant_subword (rtx op, int offset, enum machine_mode mode)
-{
- int size_ratio = HOST_BITS_PER_WIDE_INT / BITS_PER_WORD;
- HOST_WIDE_INT val;
-
- /* If OP is already an integer word, return it. */
- if (GET_MODE_CLASS (mode) == MODE_INT
- && GET_MODE_SIZE (mode) == UNITS_PER_WORD)
- return op;
-
- /* The output is some bits, the width of the target machine's word.
- A wider-word host can surely hold them in a CONST_INT. A narrower-word
- host can't. */
- if (HOST_BITS_PER_WIDE_INT >= BITS_PER_WORD
- && GET_MODE_CLASS (mode) == MODE_FLOAT
- && GET_MODE_BITSIZE (mode) == 64
- && GET_CODE (op) == CONST_DOUBLE)
- {
- long k[2];
- REAL_VALUE_TYPE rv;
-
- REAL_VALUE_FROM_CONST_DOUBLE (rv, op);
- REAL_VALUE_TO_TARGET_DOUBLE (rv, k);
-
- /* We handle 32-bit and >= 64-bit words here. Note that the order in
- which the words are written depends on the word endianness.
- ??? This is a potential portability problem and should
- be fixed at some point.
-
- We must exercise caution with the sign bit. By definition there
- are 32 significant bits in K; there may be more in a HOST_WIDE_INT.
- Consider a host with a 32-bit long and a 64-bit HOST_WIDE_INT.
- So we explicitly mask and sign-extend as necessary. */
- if (BITS_PER_WORD == 32)
- {
- val = k[offset];
- val = ((val & 0xffffffff) ^ 0x80000000) - 0x80000000;
- return GEN_INT (val);
- }
-#if HOST_BITS_PER_WIDE_INT >= 64
- else if (BITS_PER_WORD >= 64 && offset == 0)
- {
- val = k[! WORDS_BIG_ENDIAN];
- val = (((val & 0xffffffff) ^ 0x80000000) - 0x80000000) << 32;
- val |= (HOST_WIDE_INT) k[WORDS_BIG_ENDIAN] & 0xffffffff;
- return GEN_INT (val);
- }
-#endif
- else if (BITS_PER_WORD == 16)
- {
- val = k[offset >> 1];
- if ((offset & 1) == ! WORDS_BIG_ENDIAN)
- val >>= 16;
- val = ((val & 0xffff) ^ 0x8000) - 0x8000;
- return GEN_INT (val);
- }
- else
- abort ();
- }
- else if (HOST_BITS_PER_WIDE_INT >= BITS_PER_WORD
- && GET_MODE_CLASS (mode) == MODE_FLOAT
- && GET_MODE_BITSIZE (mode) > 64
- && GET_CODE (op) == CONST_DOUBLE)
- {
- long k[4];
- REAL_VALUE_TYPE rv;
-
- REAL_VALUE_FROM_CONST_DOUBLE (rv, op);
- REAL_VALUE_TO_TARGET_LONG_DOUBLE (rv, k);
-
- if (BITS_PER_WORD == 32)
- {
- val = k[offset];
- val = ((val & 0xffffffff) ^ 0x80000000) - 0x80000000;
- return GEN_INT (val);
- }
-#if HOST_BITS_PER_WIDE_INT >= 64
- else if (BITS_PER_WORD >= 64 && offset <= 1)
- {
- val = k[offset * 2 + ! WORDS_BIG_ENDIAN];
- val = (((val & 0xffffffff) ^ 0x80000000) - 0x80000000) << 32;
- val |= (HOST_WIDE_INT) k[offset * 2 + WORDS_BIG_ENDIAN] & 0xffffffff;
- return GEN_INT (val);
- }
-#endif
- else
- abort ();
- }
-
- /* Single word float is a little harder, since single- and double-word
- values often do not have the same high-order bits. We have already
- verified that we want the only defined word of the single-word value. */
- if (GET_MODE_CLASS (mode) == MODE_FLOAT
- && GET_MODE_BITSIZE (mode) == 32
- && GET_CODE (op) == CONST_DOUBLE)
- {
- long l;
- REAL_VALUE_TYPE rv;
-
- REAL_VALUE_FROM_CONST_DOUBLE (rv, op);
- REAL_VALUE_TO_TARGET_SINGLE (rv, l);
-
- /* Sign extend from known 32-bit value to HOST_WIDE_INT. */
- val = l;
- val = ((val & 0xffffffff) ^ 0x80000000) - 0x80000000;
-
- if (BITS_PER_WORD == 16)
- {
- if ((offset & 1) == ! WORDS_BIG_ENDIAN)
- val >>= 16;
- val = ((val & 0xffff) ^ 0x8000) - 0x8000;
- }
-
- return GEN_INT (val);
- }
-
- /* The only remaining cases that we can handle are integers.
- Convert to proper endianness now since these cases need it.
- At this point, offset == 0 means the low-order word.
-
- We do not want to handle the case when BITS_PER_WORD <= HOST_BITS_PER_INT
- in general. However, if OP is (const_int 0), we can just return
- it for any word. */
-
- if (op == const0_rtx)
- return op;
-
- if (GET_MODE_CLASS (mode) != MODE_INT
- || (GET_CODE (op) != CONST_INT && GET_CODE (op) != CONST_DOUBLE)
- || BITS_PER_WORD > HOST_BITS_PER_WIDE_INT)
- return 0;
-
- if (WORDS_BIG_ENDIAN)
- offset = GET_MODE_SIZE (mode) / UNITS_PER_WORD - 1 - offset;
-
- /* Find out which word on the host machine this value is in and get
- it from the constant. */
- val = (offset / size_ratio == 0
- ? (GET_CODE (op) == CONST_INT ? INTVAL (op) : CONST_DOUBLE_LOW (op))
- : (GET_CODE (op) == CONST_INT
- ? (INTVAL (op) < 0 ? ~0 : 0) : CONST_DOUBLE_HIGH (op)));
-
- /* Get the value we want into the low bits of val. */
- if (BITS_PER_WORD < HOST_BITS_PER_WIDE_INT)
- val = ((val >> ((offset % size_ratio) * BITS_PER_WORD)));
-
- val = trunc_int_for_mode (val, word_mode);
-
- return GEN_INT (val);
-}
-
/* Return subword OFFSET of operand OP.
The word number, OFFSET, is interpreted as the word number starting
at the low-order address. OFFSET 0 is the low-order word if not
static int simplify_plus_minus_op_data_cmp (const void *, const void *);
static rtx simplify_plus_minus (enum rtx_code, enum machine_mode, rtx,
rtx, int);
+static rtx simplify_immed_subreg (enum machine_mode, rtx, enum machine_mode,
+ unsigned int);
static bool associative_constant_p (rtx);
static rtx simplify_associative_operation (enum rtx_code, enum machine_mode,
rtx, rtx);
return 0;
}
-/* Simplify SUBREG:OUTERMODE(OP:INNERMODE, BYTE)
- Return 0 if no simplifications is possible. */
-rtx
-simplify_subreg (enum machine_mode outermode, rtx op,
- enum machine_mode innermode, unsigned int byte)
-{
- /* Little bit of sanity checking. */
- if (innermode == VOIDmode || outermode == VOIDmode
- || innermode == BLKmode || outermode == BLKmode)
- abort ();
-
- if (GET_MODE (op) != innermode
- && GET_MODE (op) != VOIDmode)
- abort ();
+/* Evaluate a SUBREG of a CONST_INT or CONST_DOUBLE or CONST_VECTOR,
+ returning another CONST_INT or CONST_DOUBLE or CONST_VECTOR.
- if (byte % GET_MODE_SIZE (outermode)
- || byte >= GET_MODE_SIZE (innermode))
- abort ();
+ Works by unpacking OP into a collection of 8-bit values
+ represented as a little-endian array of 'unsigned char', selecting by BYTE,
+ and then repacking them again for OUTERMODE. */
- if (outermode == innermode && !byte)
+static rtx
+simplify_immed_subreg (enum machine_mode outermode, rtx op,
+ enum machine_mode innermode, unsigned int byte)
+{
+ /* We support up to 512-bit values (for V8DFmode). */
+ enum {
+ max_bitsize = 512,
+ value_bit = 8,
+ value_mask = (1 << value_bit) - 1
+ };
+ unsigned char value[max_bitsize / value_bit];
+ int value_start;
+ int i;
+ int elem;
+
+ int num_elem;
+ rtx * elems;
+ int elem_bitsize;
+ rtx result_s;
+ rtvec result_v = NULL;
+ enum mode_class outer_class;
+ enum machine_mode outer_submode;
+
+ /* Some ports misuse CCmode. */
+ if (GET_MODE_CLASS (outermode) == MODE_CC && GET_CODE (op) == CONST_INT)
return op;
- /* Simplify subregs of vector constants. */
+ /* Unpack the value. */
+
if (GET_CODE (op) == CONST_VECTOR)
{
- int elt_size = GET_MODE_SIZE (GET_MODE_INNER (innermode));
- const unsigned int offset = byte / elt_size;
- rtx elt;
+ num_elem = CONST_VECTOR_NUNITS (op);
+ elems = &CONST_VECTOR_ELT (op, 0);
+ elem_bitsize = GET_MODE_BITSIZE (GET_MODE_INNER (innermode));
+ }
+ else
+ {
+ num_elem = 1;
+ elems = &op;
+ elem_bitsize = max_bitsize;
+ }
- if (GET_MODE_INNER (innermode) == outermode)
+ if (BITS_PER_UNIT % value_bit != 0)
+ abort (); /* Too complicated; reducing value_bit may help. */
+ if (elem_bitsize % BITS_PER_UNIT != 0)
+ abort (); /* I don't know how to handle endianness of sub-units. */
+
+ for (elem = 0; elem < num_elem; elem++)
+ {
+ unsigned char * vp;
+ rtx el = elems[elem];
+
+ /* Vectors are kept in target memory order. (This is probably
+ a mistake.) */
+ {
+ unsigned byte = (elem * elem_bitsize) / BITS_PER_UNIT;
+ unsigned ibyte = (((num_elem - 1 - elem) * elem_bitsize)
+ / BITS_PER_UNIT);
+ unsigned word_byte = WORDS_BIG_ENDIAN ? ibyte : byte;
+ unsigned subword_byte = BYTES_BIG_ENDIAN ? ibyte : byte;
+ unsigned bytele = (subword_byte % UNITS_PER_WORD
+ + (word_byte / UNITS_PER_WORD) * UNITS_PER_WORD);
+ vp = value + (bytele * BITS_PER_UNIT) / value_bit;
+ }
+
+ switch (GET_CODE (el))
{
- elt = CONST_VECTOR_ELT (op, offset);
-
- /* ?? We probably don't need this copy_rtx because constants
- can be shared. ?? */
+ case CONST_INT:
+ for (i = 0;
+ i < HOST_BITS_PER_WIDE_INT && i < elem_bitsize;
+ i += value_bit)
+ *vp++ = INTVAL (el) >> i;
+ /* CONST_INTs are always logically sign-extended. */
+ for (; i < elem_bitsize; i += value_bit)
+ *vp++ = INTVAL (el) < 0 ? -1 : 0;
+ break;
+
+ case CONST_DOUBLE:
+ if (GET_MODE (el) == VOIDmode)
+ {
+ /* If this triggers, someone should have generated a
+ CONST_INT instead. */
+ if (elem_bitsize <= HOST_BITS_PER_WIDE_INT)
+ abort ();
- return copy_rtx (elt);
- }
- else if (GET_MODE_INNER (innermode) == GET_MODE_INNER (outermode)
- && GET_MODE_SIZE (innermode) > GET_MODE_SIZE (outermode))
- {
- return (gen_rtx_CONST_VECTOR
- (outermode,
- gen_rtvec_v (GET_MODE_NUNITS (outermode),
- &CONST_VECTOR_ELT (op, offset))));
- }
- else if (GET_MODE_CLASS (outermode) == MODE_INT
- && (GET_MODE_SIZE (outermode) % elt_size == 0))
- {
- /* This happens when the target register size is smaller then
- the vector mode, and we synthesize operations with vectors
- of elements that are smaller than the register size. */
- HOST_WIDE_INT sum = 0, high = 0;
- unsigned n_elts = (GET_MODE_SIZE (outermode) / elt_size);
- unsigned i = BYTES_BIG_ENDIAN ? offset : offset + n_elts - 1;
- unsigned step = BYTES_BIG_ENDIAN ? 1 : -1;
- int shift = BITS_PER_UNIT * elt_size;
- unsigned HOST_WIDE_INT unit_mask;
-
- unit_mask = (unsigned HOST_WIDE_INT) -1
- >> (sizeof (HOST_WIDE_INT) * BITS_PER_UNIT - shift);
-
- for (; n_elts--; i += step)
+ for (i = 0; i < HOST_BITS_PER_WIDE_INT; i += value_bit)
+ *vp++ = CONST_DOUBLE_LOW (el) >> i;
+ while (i < HOST_BITS_PER_WIDE_INT * 2 && i < elem_bitsize)
+ {
+ *vp++ = CONST_DOUBLE_HIGH (el) >> i;
+ i += value_bit;
+ }
+ /* It shouldn't matter what's done here, so fill it with
+ zero. */
+ for (; i < max_bitsize; i += value_bit)
+ *vp++ = 0;
+ }
+ else if (GET_MODE_CLASS (GET_MODE (el)) == MODE_FLOAT)
{
- elt = CONST_VECTOR_ELT (op, i);
- if (GET_CODE (elt) == CONST_DOUBLE
- && GET_MODE_CLASS (GET_MODE (elt)) == MODE_FLOAT)
+ long tmp[max_bitsize / 32];
+ int bitsize = GET_MODE_BITSIZE (GET_MODE (el));
+
+ if (bitsize > elem_bitsize)
+ abort ();
+ if (bitsize % value_bit != 0)
+ abort ();
+
+ real_to_target (tmp, CONST_DOUBLE_REAL_VALUE (el),
+ GET_MODE (el));
+
+ /* real_to_target produces its result in words affected by
+ FLOAT_WORDS_BIG_ENDIAN. However, we ignore this,
+ and use WORDS_BIG_ENDIAN instead; see the documentation
+ of SUBREG in rtl.texi. */
+ for (i = 0; i < bitsize; i += value_bit)
{
- elt = gen_lowpart_common (int_mode_for_mode (GET_MODE (elt)),
- elt);
- if (! elt)
- return NULL_RTX;
+ int ibase;
+ if (WORDS_BIG_ENDIAN)
+ ibase = bitsize - 1 - i;
+ else
+ ibase = i;
+ *vp++ = tmp[ibase / 32] >> i % 32;
}
- if (GET_CODE (elt) != CONST_INT)
- return NULL_RTX;
- /* Avoid overflow. */
- if (high >> (HOST_BITS_PER_WIDE_INT - shift))
- return NULL_RTX;
- high = high << shift | sum >> (HOST_BITS_PER_WIDE_INT - shift);
- sum = (sum << shift) + (INTVAL (elt) & unit_mask);
+
+ /* It shouldn't matter what's done here, so fill it with
+ zero. */
+ for (; i < elem_bitsize; i += value_bit)
+ *vp++ = 0;
}
- if (GET_MODE_BITSIZE (outermode) <= HOST_BITS_PER_WIDE_INT)
- return GEN_INT (trunc_int_for_mode (sum, outermode));
- else if (GET_MODE_BITSIZE (outermode) == 2* HOST_BITS_PER_WIDE_INT)
- return immed_double_const (sum, high, outermode);
else
- return NULL_RTX;
- }
- else if (GET_MODE_CLASS (outermode) == MODE_INT
- && (elt_size % GET_MODE_SIZE (outermode) == 0))
- {
- enum machine_mode new_mode
- = int_mode_for_mode (GET_MODE_INNER (innermode));
- int subbyte = byte % elt_size;
-
- op = simplify_subreg (new_mode, op, innermode, byte - subbyte);
- if (! op)
- return NULL_RTX;
- return simplify_subreg (outermode, op, new_mode, subbyte);
+ abort ();
+ break;
+
+ default:
+ abort ();
}
- else if (GET_MODE_CLASS (outermode) == MODE_INT)
- /* This shouldn't happen, but let's not do anything stupid. */
- return NULL_RTX;
}
- /* Attempt to simplify constant to non-SUBREG expression. */
- if (CONSTANT_P (op))
+ /* Now, pick the right byte to start with. */
+ /* Renumber BYTE so that the least-significant byte is byte 0. A special
+ case is paradoxical SUBREGs, which shouldn't be adjusted since they
+ will already have offset 0. */
+ if (GET_MODE_SIZE (innermode) >= GET_MODE_SIZE (outermode))
{
- int offset, part;
- unsigned HOST_WIDE_INT val = 0;
+ unsigned ibyte = (GET_MODE_SIZE (innermode) - GET_MODE_SIZE (outermode)
+ - byte);
+ unsigned word_byte = WORDS_BIG_ENDIAN ? ibyte : byte;
+ unsigned subword_byte = BYTES_BIG_ENDIAN ? ibyte : byte;
+ byte = (subword_byte % UNITS_PER_WORD
+ + (word_byte / UNITS_PER_WORD) * UNITS_PER_WORD);
+ }
- if (VECTOR_MODE_P (outermode))
- {
- /* Construct a CONST_VECTOR from individual subregs. */
- enum machine_mode submode = GET_MODE_INNER (outermode);
- int subsize = GET_MODE_UNIT_SIZE (outermode);
- int i, elts = GET_MODE_NUNITS (outermode);
- rtvec v = rtvec_alloc (elts);
- rtx elt;
-
- for (i = 0; i < elts; i++, byte += subsize)
- {
- /* This might fail, e.g. if taking a subreg from a SYMBOL_REF. */
- /* ??? It would be nice if we could actually make such subregs
- on targets that allow such relocations. */
- if (byte >= GET_MODE_SIZE (innermode))
- elt = CONST0_RTX (submode);
- else
- elt = simplify_subreg (submode, op, innermode, byte);
- if (! elt)
- return NULL_RTX;
- RTVEC_ELT (v, i) = elt;
- }
- return gen_rtx_CONST_VECTOR (outermode, v);
- }
+ /* BYTE should still be inside OP. (Note that BYTE is unsigned,
+ so if it's become negative it will instead be very large.) */
+ if (byte >= GET_MODE_SIZE (innermode))
+ abort ();
- /* ??? This code is partly redundant with code below, but can handle
- the subregs of floats and similar corner cases.
- Later it we should move all simplification code here and rewrite
- GEN_LOWPART_IF_POSSIBLE, GEN_HIGHPART, OPERAND_SUBWORD and friends
- using SIMPLIFY_SUBREG. */
- if (subreg_lowpart_offset (outermode, innermode) == byte
- && GET_CODE (op) != CONST_VECTOR)
- {
- rtx new = gen_lowpart_if_possible (outermode, op);
- if (new)
- return new;
- }
+ /* Convert from bytes to chunks of size value_bit. */
+ value_start = byte * (BITS_PER_UNIT / value_bit);
- /* Similar comment as above apply here. */
- if (GET_MODE_SIZE (outermode) == UNITS_PER_WORD
- && GET_MODE_SIZE (innermode) > UNITS_PER_WORD
- && GET_MODE_CLASS (outermode) == MODE_INT)
- {
- rtx new = constant_subword (op,
- (byte / UNITS_PER_WORD),
- innermode);
- if (new)
- return new;
- }
+ /* Re-pack the value. */
+
+ if (VECTOR_MODE_P (outermode))
+ {
+ num_elem = GET_MODE_NUNITS (outermode);
+ result_v = rtvec_alloc (num_elem);
+ elems = &RTVEC_ELT (result_v, 0);
+ outer_submode = GET_MODE_INNER (outermode);
+ }
+ else
+ {
+ num_elem = 1;
+ elems = &result_s;
+ outer_submode = outermode;
+ }
- if (GET_MODE_CLASS (outermode) != MODE_INT
- && GET_MODE_CLASS (outermode) != MODE_CC)
- {
- enum machine_mode new_mode = int_mode_for_mode (outermode);
+ outer_class = GET_MODE_CLASS (outer_submode);
+ elem_bitsize = GET_MODE_BITSIZE (outer_submode);
- if (new_mode != innermode || byte != 0)
- {
- op = simplify_subreg (new_mode, op, innermode, byte);
- if (! op)
- return NULL_RTX;
- return simplify_subreg (outermode, op, new_mode, 0);
- }
- }
+ if (elem_bitsize % value_bit != 0)
+ abort ();
+ if (elem_bitsize + value_start * value_bit > max_bitsize)
+ abort ();
- offset = byte * BITS_PER_UNIT;
- switch (GET_CODE (op))
+ for (elem = 0; elem < num_elem; elem++)
+ {
+ unsigned char *vp;
+
+ /* Vectors are stored in target memory order. (This is probably
+ a mistake.) */
+ {
+ unsigned byte = (elem * elem_bitsize) / BITS_PER_UNIT;
+ unsigned ibyte = (((num_elem - 1 - elem) * elem_bitsize)
+ / BITS_PER_UNIT);
+ unsigned word_byte = WORDS_BIG_ENDIAN ? ibyte : byte;
+ unsigned subword_byte = BYTES_BIG_ENDIAN ? ibyte : byte;
+ unsigned bytele = (subword_byte % UNITS_PER_WORD
+ + (word_byte / UNITS_PER_WORD) * UNITS_PER_WORD);
+ vp = value + value_start + (bytele * BITS_PER_UNIT) / value_bit;
+ }
+
+ switch (outer_class)
{
- case CONST_DOUBLE:
- if (GET_MODE (op) != VOIDmode)
- break;
-
- /* We can't handle this case yet. */
- if (GET_MODE_BITSIZE (outermode) >= HOST_BITS_PER_WIDE_INT)
- return NULL_RTX;
+ case MODE_INT:
+ case MODE_PARTIAL_INT:
+ {
+ unsigned HOST_WIDE_INT hi = 0, lo = 0;
+
+ for (i = 0;
+ i < HOST_BITS_PER_WIDE_INT && i < elem_bitsize;
+ i += value_bit)
+ lo |= (HOST_WIDE_INT)(*vp++ & value_mask) << i;
+ for (; i < elem_bitsize; i += value_bit)
+ hi |= ((HOST_WIDE_INT)(*vp++ & value_mask)
+ << (i - HOST_BITS_PER_WIDE_INT));
+
+ /* immed_double_const doesn't call trunc_int_for_mode. I don't
+ know why. */
+ if (elem_bitsize <= HOST_BITS_PER_WIDE_INT)
+ elems[elem] = gen_int_mode (lo, outer_submode);
+ else
+ elems[elem] = immed_double_const (lo, hi, outer_submode);
+ }
+ break;
+
+ case MODE_FLOAT:
+ {
+ REAL_VALUE_TYPE r;
+ long tmp[max_bitsize / 32];
+
+ /* real_from_target wants its input in words affected by
+ FLOAT_WORDS_BIG_ENDIAN. However, we ignore this,
+ and use WORDS_BIG_ENDIAN instead; see the documentation
+ of SUBREG in rtl.texi. */
+ for (i = 0; i < max_bitsize / 32; i++)
+ tmp[i] = 0;
+ for (i = 0; i < elem_bitsize; i += value_bit)
+ {
+ int ibase;
+ if (WORDS_BIG_ENDIAN)
+ ibase = elem_bitsize - 1 - i;
+ else
+ ibase = i;
+ tmp[ibase / 32] = (*vp++ & value_mask) << i % 32;
+ }
- part = offset >= HOST_BITS_PER_WIDE_INT;
- if ((BITS_PER_WORD > HOST_BITS_PER_WIDE_INT
- && BYTES_BIG_ENDIAN)
- || (BITS_PER_WORD <= HOST_BITS_PER_WIDE_INT
- && WORDS_BIG_ENDIAN))
- part = !part;
- val = part ? CONST_DOUBLE_HIGH (op) : CONST_DOUBLE_LOW (op);
- offset %= HOST_BITS_PER_WIDE_INT;
+ real_from_target (&r, tmp, outer_submode);
+ elems[elem] = CONST_DOUBLE_FROM_REAL_VALUE (r, outer_submode);
+ }
+ break;
+
+ default:
+ abort ();
+ }
+ }
+ if (VECTOR_MODE_P (outermode))
+ return gen_rtx_CONST_VECTOR (outermode, result_v);
+ else
+ return result_s;
+}
- /* We've already picked the word we want from a double, so
- pretend this is actually an integer. */
- innermode = mode_for_size (HOST_BITS_PER_WIDE_INT, MODE_INT, 0);
+/* Simplify SUBREG:OUTERMODE(OP:INNERMODE, BYTE)
+ Return 0 if no simplifications are possible. */
+rtx
+simplify_subreg (enum machine_mode outermode, rtx op,
+ enum machine_mode innermode, unsigned int byte)
+{
+ /* Little bit of sanity checking. */
+ if (innermode == VOIDmode || outermode == VOIDmode
+ || innermode == BLKmode || outermode == BLKmode)
+ abort ();
- /* Fall through. */
- case CONST_INT:
- if (GET_CODE (op) == CONST_INT)
- val = INTVAL (op);
+ if (GET_MODE (op) != innermode
+ && GET_MODE (op) != VOIDmode)
+ abort ();
- /* We don't handle synthesizing of non-integral constants yet. */
- if (GET_MODE_CLASS (outermode) != MODE_INT)
- return NULL_RTX;
+ if (byte % GET_MODE_SIZE (outermode)
+ || byte >= GET_MODE_SIZE (innermode))
+ abort ();
- if (BYTES_BIG_ENDIAN || WORDS_BIG_ENDIAN)
- {
- if (WORDS_BIG_ENDIAN)
- offset = (GET_MODE_BITSIZE (innermode)
- - GET_MODE_BITSIZE (outermode) - offset);
- if (BYTES_BIG_ENDIAN != WORDS_BIG_ENDIAN
- && GET_MODE_SIZE (outermode) < UNITS_PER_WORD)
- offset = (offset + BITS_PER_WORD - GET_MODE_BITSIZE (outermode)
- - 2 * (offset % BITS_PER_WORD));
- }
+ if (outermode == innermode && !byte)
+ return op;
- if (offset >= HOST_BITS_PER_WIDE_INT)
- return ((HOST_WIDE_INT) val < 0) ? constm1_rtx : const0_rtx;
- else
- {
- val >>= offset;
- if (GET_MODE_BITSIZE (outermode) < HOST_BITS_PER_WIDE_INT)
- val = trunc_int_for_mode (val, outermode);
- return GEN_INT (val);
- }
- default:
- break;
- }
- }
+ if (GET_CODE (op) == CONST_INT
+ || GET_CODE (op) == CONST_DOUBLE
+ || GET_CODE (op) == CONST_VECTOR)
+ return simplify_immed_subreg (outermode, op, innermode, byte);
/* Changing mode twice with SUBREG => just change it once,
or not at all if changing back op starting mode. */
return NULL_RTX;
}
+
/* Make a SUBREG operation or equivalent if it folds. */
rtx