-
-/* Helper routine for all the constant cases of operand_subword.
- Some places invoke this directly. */
-
-rtx
-constant_subword (op, offset, mode)
- 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);
-}
-