2011-06-23 Andrew Stubbs <ams@codesourcery.com>
gcc/
* config/arm/arm.md (maddhidi4): Remove '*' from name.
* expr.c (expand_expr_real_2): Use find_widening_optab_handler.
* optabs.c (find_widening_optab_handler): New function.
(expand_widen_pattern_expr): Use find_widening_optab_handler.
(expand_binop_directly): Likewise.
(expand_binop): Likewise.
* optabs.h (find_widening_optab_handler): New prototype.
* tree-cfg.c (verify_gimple_assign_binary): Adjust WIDEN_MULT_EXPR
type precision rules.
(verify_gimple_assign_ternary): Likewise for WIDEN_MULT_PLUS_EXPR.
* tree-ssa-math-opts.c (is_widening_mult_rhs_p): Allow widening by
more than one mode.
Explicitly disallow mis-matched input types.
(convert_mult_to_widen): Use find_widening_optab_handler.
(convert_plusminus_to_widen): Likewise.
@@ -1857,7 +1857,7 @@
(set_attr "predicable" "yes")]
)
-(define_insn "*maddhidi4"
+(define_insn "maddhidi4"
[(set (match_operand:DI 0 "s_register_operand" "=r")
(plus:DI
(mult:DI (sign_extend:DI
@@ -7632,19 +7632,16 @@ expand_expr_real_2 (sepops ops, rtx target, enum machine_mode tmode,
{
enum machine_mode innermode = TYPE_MODE (TREE_TYPE (treeop0));
this_optab = usmul_widen_optab;
- if (mode == GET_MODE_2XWIDER_MODE (innermode))
+ if (find_widening_optab_handler (this_optab, mode, innermode, 0)
+ != CODE_FOR_nothing)
{
- if (widening_optab_handler (this_optab, mode, innermode)
- != CODE_FOR_nothing)
- {
- if (TYPE_UNSIGNED (TREE_TYPE (treeop0)))
- expand_operands (treeop0, treeop1, NULL_RTX, &op0, &op1,
- EXPAND_NORMAL);
- else
- expand_operands (treeop0, treeop1, NULL_RTX, &op1, &op0,
- EXPAND_NORMAL);
- goto binop3;
- }
+ if (TYPE_UNSIGNED (TREE_TYPE (treeop0)))
+ expand_operands (treeop0, treeop1, NULL_RTX, &op0, &op1,
+ EXPAND_NORMAL);
+ else
+ expand_operands (treeop0, treeop1, NULL_RTX, &op1, &op0,
+ EXPAND_NORMAL);
+ goto binop3;
}
}
/* Check for a multiplication with matching signedness. */
@@ -7659,10 +7656,9 @@ expand_expr_real_2 (sepops ops, rtx target, enum machine_mode tmode,
optab other_optab = zextend_p ? smul_widen_optab : umul_widen_optab;
this_optab = zextend_p ? umul_widen_optab : smul_widen_optab;
- if (mode == GET_MODE_2XWIDER_MODE (innermode)
- && TREE_CODE (treeop0) != INTEGER_CST)
+ if (TREE_CODE (treeop0) != INTEGER_CST)
{
- if (widening_optab_handler (this_optab, mode, innermode)
+ if (find_widening_optab_handler (this_optab, mode, innermode, 0)
!= CODE_FOR_nothing)
{
expand_operands (treeop0, treeop1, NULL_RTX, &op0, &op1,
@@ -7671,7 +7667,7 @@ expand_expr_real_2 (sepops ops, rtx target, enum machine_mode tmode,
unsignedp, this_optab);
return REDUCE_BIT_FIELD (temp);
}
- if (widening_optab_handler (other_optab, mode, innermode)
+ if (find_widening_optab_handler (other_optab, mode, innermode, 0)
!= CODE_FOR_nothing
&& innermode == word_mode)
{
@@ -225,6 +225,32 @@ add_equal_note (rtx insns, rtx target, enum rtx_code code, rtx op0, rtx op1)
return 1;
}
+/* Find a widening optab even if it doesn't widen as much as we want.
+ E.g. if from_mode is HImode, and to_mode is DImode, and there is no
+ direct HI->SI insn, then return SI->DI, if that exists.
+ If PERMIT_NON_WIDENING is non-zero then this can be used with
+ non-widening optabs also. */
+
+enum insn_code
+find_widening_optab_handler (optab op, enum machine_mode to_mode,
+ enum machine_mode from_mode,
+ int permit_non_widening)
+{
+ for (; (permit_non_widening || from_mode != to_mode)
+ && GET_MODE_SIZE (from_mode) <= GET_MODE_SIZE (to_mode)
+ && from_mode != VOIDmode;
+ from_mode = GET_MODE_WIDER_MODE (from_mode))
+ {
+ enum insn_code handler = widening_optab_handler (op, to_mode,
+ from_mode);
+
+ if (handler != CODE_FOR_nothing)
+ return handler;
+ }
+
+ return CODE_FOR_nothing;
+}
+
/* Widen OP to MODE and return the rtx for the widened operand. UNSIGNEDP
says whether OP is signed or unsigned. NO_EXTEND is nonzero if we need
not actually do a sign-extend or zero-extend, but can leave the
@@ -515,8 +541,9 @@ expand_widen_pattern_expr (sepops ops, rtx op0, rtx op1, rtx wide_op,
optab_for_tree_code (ops->code, TREE_TYPE (oprnd0), optab_default);
if (ops->code == WIDEN_MULT_PLUS_EXPR
|| ops->code == WIDEN_MULT_MINUS_EXPR)
- icode = widening_optab_handler (widen_pattern_optab,
- TYPE_MODE (TREE_TYPE (ops->op2)), tmode0);
+ icode = find_widening_optab_handler (widen_pattern_optab,
+ TYPE_MODE (TREE_TYPE (ops->op2)),
+ tmode0, 0);
else
icode = optab_handler (widen_pattern_optab, tmode0);
gcc_assert (icode != CODE_FOR_nothing);
@@ -1243,7 +1270,8 @@ expand_binop_directly (enum machine_mode mode, optab binoptab,
rtx last)
{
enum machine_mode from_mode = GET_MODE (op0);
- enum insn_code icode = widening_optab_handler (binoptab, mode, from_mode);
+ enum insn_code icode = find_widening_optab_handler (binoptab, mode,
+ from_mode, 1);
enum machine_mode xmode0 = insn_data[(int) icode].operand[1].mode;
enum machine_mode xmode1 = insn_data[(int) icode].operand[2].mode;
enum machine_mode mode0, mode1, tmp_mode;
@@ -1390,7 +1418,7 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1,
/* If we can do it with a three-operand insn, do so. */
if (methods != OPTAB_MUST_WIDEN
- && widening_optab_handler (binoptab, mode, GET_MODE (op0))
+ && find_widening_optab_handler (binoptab, mode, GET_MODE (op0), 1)
!= CODE_FOR_nothing)
{
temp = expand_binop_directly (mode, binoptab, op0, op1, target,
@@ -1461,14 +1489,15 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1,
wider_mode != VOIDmode;
wider_mode = GET_MODE_WIDER_MODE (wider_mode))
{
- if (widening_optab_handler (binoptab, wider_mode, mode)
+ if (find_widening_optab_handler (binoptab, wider_mode, mode, 1)
!= CODE_FOR_nothing
|| (binoptab == smul_optab
&& GET_MODE_WIDER_MODE (wider_mode) != VOIDmode
- && (widening_optab_handler ((unsignedp ? umul_widen_optab
- : smul_widen_optab),
- GET_MODE_WIDER_MODE (wider_mode),
- mode)
+ && (find_widening_optab_handler ((unsignedp
+ ? umul_widen_optab
+ : smul_widen_optab),
+ GET_MODE_WIDER_MODE (wider_mode),
+ mode, 0)
!= CODE_FOR_nothing)))
{
rtx xop0 = op0, xop1 = op1;
@@ -2003,7 +2032,7 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1,
wider_mode != VOIDmode;
wider_mode = GET_MODE_WIDER_MODE (wider_mode))
{
- if (widening_optab_handler (binoptab, wider_mode, mode)
+ if (find_widening_optab_handler (binoptab, wider_mode, mode, 1)
!= CODE_FOR_nothing
|| (methods == OPTAB_LIB
&& optab_libfunc (binoptab, wider_mode)))
@@ -807,6 +807,10 @@ extern rtx expand_copysign (rtx, rtx, rtx);
extern void emit_unop_insn (enum insn_code, rtx, rtx, enum rtx_code);
extern bool maybe_emit_unop_insn (enum insn_code, rtx, rtx, enum rtx_code);
+/* Find a widening optab even if it doesn't widen as much as we want. */
+extern enum insn_code find_widening_optab_handler (optab, enum machine_mode,
+ enum machine_mode, int);
+
/* An extra flag to control optab_for_tree_code's behavior. This is needed to
distinguish between machines with a vector shift that takes a scalar for the
shift amount vs. machines that take a vector for the shift amount. */
@@ -3577,7 +3577,7 @@ do_pointer_plus_expr_check:
case WIDEN_MULT_EXPR:
if (TREE_CODE (lhs_type) != INTEGER_TYPE)
return true;
- return ((2 * TYPE_PRECISION (rhs1_type) != TYPE_PRECISION (lhs_type))
+ return ((2 * TYPE_PRECISION (rhs1_type) > TYPE_PRECISION (lhs_type))
|| (TYPE_PRECISION (rhs1_type) != TYPE_PRECISION (rhs2_type)));
case WIDEN_SUM_EXPR:
@@ -3668,7 +3668,7 @@ verify_gimple_assign_ternary (gimple stmt)
&& !FIXED_POINT_TYPE_P (rhs1_type))
|| !useless_type_conversion_p (rhs1_type, rhs2_type)
|| !useless_type_conversion_p (lhs_type, rhs3_type)
- || 2 * TYPE_PRECISION (rhs1_type) != TYPE_PRECISION (lhs_type)
+ || 2 * TYPE_PRECISION (rhs1_type) > TYPE_PRECISION (lhs_type)
|| TYPE_PRECISION (rhs1_type) != TYPE_PRECISION (rhs2_type))
{
error ("type mismatch in widening multiply-accumulate expression");
@@ -1950,8 +1950,8 @@ struct gimple_opt_pass pass_optimize_bswap =
/* Return true if RHS is a suitable operand for a widening multiplication.
There are two cases:
- - RHS makes some value twice as wide. Store that value in *NEW_RHS_OUT
- if so, and store its type in *TYPE_OUT.
+ - RHS makes some value at least twice as wide. Store that value
+ in *NEW_RHS_OUT if so, and store its type in *TYPE_OUT.
- RHS is an integer constant. Store that value in *NEW_RHS_OUT if so,
but leave *TYPE_OUT untouched. */
@@ -1979,7 +1979,7 @@ is_widening_mult_rhs_p (tree rhs, tree *type_out, tree *new_rhs_out)
rhs1 = gimple_assign_rhs1 (stmt);
type1 = TREE_TYPE (rhs1);
if (TREE_CODE (type1) != TREE_CODE (type)
- || TYPE_PRECISION (type1) * 2 != TYPE_PRECISION (type))
+ || TYPE_PRECISION (type1) * 2 > TYPE_PRECISION (type))
return false;
*new_rhs_out = rhs1;
@@ -2035,6 +2035,10 @@ is_widening_mult_p (gimple stmt,
*type2_out = *type1_out;
}
+ /* FIXME: remove this restriction. */
+ if (TYPE_PRECISION (*type1_out) != TYPE_PRECISION (*type2_out))
+ return false;
+
return true;
}
@@ -2068,7 +2072,7 @@ convert_mult_to_widen (gimple stmt)
else
op = usmul_widen_optab;
- handler = widening_optab_handler (op, to_mode, from_mode);
+ handler = find_widening_optab_handler (op, to_mode, from_mode, 0);
if (handler == CODE_FOR_nothing)
return false;
@@ -2171,8 +2175,10 @@ convert_plusminus_to_widen (gimple_stmt_iterator *gsi, gimple stmt,
accumulate in this mode/signedness combination, otherwise
this transformation is likely to pessimize code. */
this_optab = optab_for_tree_code (wmult_code, type1, optab_default);
- if (widening_optab_handler (this_optab, TYPE_MODE (type), TYPE_MODE (type1))
- == CODE_FOR_nothing)
+ handler = find_widening_optab_handler (this_optab, TYPE_MODE (type),
+ TYPE_MODE (type1), 0);
+
+ if (handler == CODE_FOR_nothing)
return false;
/* ??? May need some type verification here? */