diff --git a/gcc/simplify-rtx.c b/gcc/simplify-rtx.c index 4f0652f553e..869f0d11b2e 100644 --- a/gcc/simplify-rtx.c +++ b/gcc/simplify-rtx.c @@ -1527,6 +1527,38 @@ simplify_unary_operation_1 (enum rtx_code code, machine_mode mode, rtx op) && XEXP (op, 1) != const0_rtx) return simplify_gen_unary (ZERO_EXTEND, mode, op, GET_MODE (op)); + /* (sign_extend:M (truncate:N (lshiftrt:O (const_int I)))) where + I is GET_MODE_PRECISION(O) - GET_MODE_PRECISION(N), simplifies to + (ashiftrt:M (const_int I)) if modes M and O are the same, and + (truncate:M (ashiftrt:O (const_int I))) if M is narrower than + O, and (sign_extend:M (ashiftrt:O (const_int I))) if M is + wider than O. */ + if (GET_CODE (op) == TRUNCATE + && GET_CODE (XEXP (op, 0)) == LSHIFTRT + && CONST_INT_P (XEXP (XEXP (op, 0), 1))) + { + scalar_int_mode m_mode, n_mode, o_mode; + rtx old_shift = XEXP (op, 0); + if (is_a (mode, &m_mode) + && is_a (GET_MODE (op), &n_mode) + && is_a (GET_MODE (old_shift), &o_mode) + && GET_MODE_PRECISION (o_mode) - GET_MODE_PRECISION (n_mode) + == INTVAL (XEXP (old_shift, 1))) + { + rtx new_shift = simplify_gen_binary (ASHIFTRT, + GET_MODE (old_shift), + XEXP (old_shift, 0), + XEXP (old_shift, 1)); + if (GET_MODE_PRECISION (m_mode) > GET_MODE_PRECISION (o_mode)) + return simplify_gen_unary (SIGN_EXTEND, mode, new_shift, + GET_MODE (new_shift)); + if (mode != GET_MODE (new_shift)) + return simplify_gen_unary (TRUNCATE, mode, new_shift, + GET_MODE (new_shift)); + return new_shift; + } + } + #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