aboutsummaryrefslogtreecommitdiff
path: root/gcc/optabs.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/optabs.c')
-rw-r--r--gcc/optabs.c23
1 files changed, 21 insertions, 2 deletions
diff --git a/gcc/optabs.c b/gcc/optabs.c
index 3230e8ac1e1..e9702e15bb0 100644
--- a/gcc/optabs.c
+++ b/gcc/optabs.c
@@ -1419,6 +1419,7 @@ expand_binop_directly (machine_mode mode, optab binoptab,
rtx pat;
rtx xop0 = op0, xop1 = op1;
rtx swap;
+ bool canonicalize_op1 = false;
/* If it is a commutative operator and the modes would match
if we would swap the operands, we can save the conversions. */
@@ -1436,6 +1437,11 @@ expand_binop_directly (machine_mode mode, optab binoptab,
xop0 = avoid_expensive_constant (xmode0, binoptab, 0, xop0, unsignedp);
if (!shift_optab_p (binoptab))
xop1 = avoid_expensive_constant (xmode1, binoptab, 1, xop1, unsignedp);
+ else if (xmode1 != VOIDmode)
+ /* Shifts and rotates often use a different mode for op1 from op0;
+ for VOIDmode constants we don't know the mode, so force it
+ to be canonicalized using convert_modes. */
+ canonicalize_op1 = true;
/* In case the insn wants input operands in modes different from
those of the actual operands, convert the operands. It would
@@ -1450,7 +1456,8 @@ expand_binop_directly (machine_mode mode, optab binoptab,
mode0 = xmode0;
}
- mode1 = GET_MODE (xop1) != VOIDmode ? GET_MODE (xop1) : mode;
+ mode1 = ((GET_MODE (xop1) != VOIDmode || canonicalize_op1)
+ ? GET_MODE (xop1) : mode);
if (xmode1 != VOIDmode && xmode1 != mode1)
{
xop1 = convert_modes (xmode1, mode1, xop1, unsignedp);
@@ -1535,7 +1542,7 @@ expand_binop (machine_mode mode, optab binoptab, rtx op0, rtx op1,
= (methods == OPTAB_LIB || methods == OPTAB_LIB_WIDEN
? OPTAB_WIDEN : methods);
enum mode_class mclass;
- machine_mode wider_mode;
+ machine_mode wider_mode, inner_mode;
rtx libfunc;
rtx temp;
rtx_insn *entry_last = get_last_insn ();
@@ -1551,6 +1558,18 @@ expand_binop (machine_mode mode, optab binoptab, rtx op0, rtx op1,
op1 = negate_rtx (mode, op1);
binoptab = add_optab;
}
+ /* For shifts, constant invalid op1 might be expanded from different
+ mode than MODE. As those are invalid, force them to a register
+ to avoid further problems during expansion. */
+ else if (CONST_INT_P (op1)
+ && shift_optab_p (binoptab)
+ && (inner_mode = (GET_MODE_INNER (mode) == VOIDmode
+ ? mode : GET_MODE_INNER (mode))) != VOIDmode
+ && UINTVAL (op1) >= GET_MODE_BITSIZE (inner_mode))
+ {
+ op1 = gen_int_mode (INTVAL (op1), inner_mode);
+ op1 = force_reg (inner_mode, op1);
+ }
/* Record where to delete back to if we backtrack. */
last = get_last_insn ();