+ /* Optimize SUBREG truncations of zero and sign extended values. */
+ if ((GET_CODE (op) == ZERO_EXTEND
+ || GET_CODE (op) == SIGN_EXTEND)
+ && GET_MODE_BITSIZE (outermode) < GET_MODE_BITSIZE (innermode))
+ {
+ unsigned int bitpos = subreg_lsb_1 (outermode, innermode, byte);
+
+ /* If we're requesting the lowpart of a zero or sign extension,
+ there are three possibilities. If the outermode is the same
+ as the origmode, we can omit both the extension and the subreg.
+ If the outermode is not larger than the origmode, we can apply
+ the truncation without the extension. Finally, if the outermode
+ is larger than the origmode, but both are integer modes, we
+ can just extend to the appropriate mode. */
+ if (bitpos == 0)
+ {
+ enum machine_mode origmode = GET_MODE (XEXP (op, 0));
+ if (outermode == origmode)
+ return XEXP (op, 0);
+ if (GET_MODE_BITSIZE (outermode) <= GET_MODE_BITSIZE (origmode))
+ return simplify_gen_subreg (outermode, XEXP (op, 0), origmode,
+ subreg_lowpart_offset (outermode,
+ origmode));
+ if (SCALAR_INT_MODE_P (outermode))
+ return simplify_gen_unary (GET_CODE (op), outermode,
+ XEXP (op, 0), origmode);
+ }
+
+ /* A SUBREG resulting from a zero extension may fold to zero if
+ it extracts higher bits that the ZERO_EXTEND's source bits. */
+ if (GET_CODE (op) == ZERO_EXTEND
+ && bitpos >= GET_MODE_BITSIZE (GET_MODE (XEXP (op, 0))))
+ return CONST0_RTX (outermode);
+ }
+
+ /* Simplify (subreg:QI (lshiftrt:SI (sign_extend:SI (x:QI)) C), 0) into
+ to (ashiftrt:QI (x:QI) C), where C is a suitable small constant and
+ the outer subreg is effectively a truncation to the original mode. */
+ if ((GET_CODE (op) == LSHIFTRT
+ || GET_CODE (op) == ASHIFTRT)
+ && SCALAR_INT_MODE_P (outermode)
+ /* Ensure that OUTERMODE is at least twice as wide as the INNERMODE
+ to avoid the possibility that an outer LSHIFTRT shifts by more
+ than the sign extension's sign_bit_copies and introduces zeros
+ into the high bits of the result. */
+ && (2 * GET_MODE_BITSIZE (outermode)) <= GET_MODE_BITSIZE (innermode)
+ && GET_CODE (XEXP (op, 1)) == CONST_INT
+ && GET_CODE (XEXP (op, 0)) == SIGN_EXTEND
+ && GET_MODE (XEXP (XEXP (op, 0), 0)) == outermode
+ && INTVAL (XEXP (op, 1)) < GET_MODE_BITSIZE (outermode)
+ && subreg_lsb_1 (outermode, innermode, byte) == 0)
+ return simplify_gen_binary (ASHIFTRT, outermode,
+ XEXP (XEXP (op, 0), 0), XEXP (op, 1));
+
+ /* Likewise (subreg:QI (lshiftrt:SI (zero_extend:SI (x:QI)) C), 0) into
+ to (lshiftrt:QI (x:QI) C), where C is a suitable small constant and
+ the outer subreg is effectively a truncation to the original mode. */
+ if ((GET_CODE (op) == LSHIFTRT
+ || GET_CODE (op) == ASHIFTRT)
+ && SCALAR_INT_MODE_P (outermode)
+ && GET_MODE_BITSIZE (outermode) < GET_MODE_BITSIZE (innermode)
+ && GET_CODE (XEXP (op, 1)) == CONST_INT
+ && GET_CODE (XEXP (op, 0)) == ZERO_EXTEND
+ && GET_MODE (XEXP (XEXP (op, 0), 0)) == outermode
+ && INTVAL (XEXP (op, 1)) < GET_MODE_BITSIZE (outermode)
+ && subreg_lsb_1 (outermode, innermode, byte) == 0)
+ return simplify_gen_binary (LSHIFTRT, outermode,
+ XEXP (XEXP (op, 0), 0), XEXP (op, 1));
+
+ /* Likewise (subreg:QI (ashift:SI (zero_extend:SI (x:QI)) C), 0) into
+ to (ashift:QI (x:QI) C), where C is a suitable small constant and
+ the outer subreg is effectively a truncation to the original mode. */
+ if (GET_CODE (op) == ASHIFT
+ && SCALAR_INT_MODE_P (outermode)
+ && GET_MODE_BITSIZE (outermode) < GET_MODE_BITSIZE (innermode)
+ && GET_CODE (XEXP (op, 1)) == CONST_INT
+ && (GET_CODE (XEXP (op, 0)) == ZERO_EXTEND
+ || GET_CODE (XEXP (op, 0)) == SIGN_EXTEND)
+ && GET_MODE (XEXP (XEXP (op, 0), 0)) == outermode
+ && INTVAL (XEXP (op, 1)) < GET_MODE_BITSIZE (outermode)
+ && subreg_lsb_1 (outermode, innermode, byte) == 0)
+ return simplify_gen_binary (ASHIFT, outermode,
+ XEXP (XEXP (op, 0), 0), XEXP (op, 1));
+