diff -urN a/gcc/tree-ssa-math-opts.c b/gcc/tree-ssa-math-opts.c --- a/gcc/tree-ssa-math-opts.c 2011-12-20 22:33:48.000000000 +0100 +++ b/gcc/tree-ssa-math-opts.c 2013-04-29 16:39:54.413585206 +0200 @@ -1968,6 +1968,47 @@ } }; +/* Return true if stmt is a type conversion operation that can be stripped + when used in a widening multiply operation. */ +static bool +widening_mult_conversion_strippable_p (tree result_type, gimple stmt) +{ + enum tree_code rhs_code = gimple_assign_rhs_code (stmt); + + if (TREE_CODE (result_type) == INTEGER_TYPE) + { + tree op_type; + tree inner_op_type; + + if (!CONVERT_EXPR_CODE_P (rhs_code)) + return false; + + op_type = TREE_TYPE (gimple_assign_lhs (stmt)); + + /* If the type of OP has the same precision as the result, then + we can strip this conversion. The multiply operation will be + selected to create the correct extension as a by-product. */ + if (TYPE_PRECISION (result_type) == TYPE_PRECISION (op_type)) + return true; + + /* We can also strip a conversion if it preserves the signed-ness of + the operation and doesn't narrow the range. */ + inner_op_type = TREE_TYPE (gimple_assign_rhs1 (stmt)); + + /* If the inner-most type is unsigned, then we can strip any + intermediate widening operation. If it's signed, then the + intermediate widening operation must also be signed. */ + if ((TYPE_UNSIGNED (inner_op_type) + || TYPE_UNSIGNED (op_type) == TYPE_UNSIGNED (inner_op_type)) + && TYPE_PRECISION (op_type) > TYPE_PRECISION (inner_op_type)) + return true; + + return false; + } + + return rhs_code == FIXED_CONVERT_EXPR; +} + /* Return true if RHS is a suitable operand for a widening multiplication, assuming a target type of TYPE. There are two cases: @@ -1984,17 +2025,13 @@ { gimple stmt; tree type1, rhs1; - enum tree_code rhs_code; if (TREE_CODE (rhs) == SSA_NAME) { stmt = SSA_NAME_DEF_STMT (rhs); if (is_gimple_assign (stmt)) { - rhs_code = gimple_assign_rhs_code (stmt); - if (TREE_CODE (type) == INTEGER_TYPE - ? !CONVERT_EXPR_CODE_P (rhs_code) - : rhs_code != FIXED_CONVERT_EXPR) + if (! widening_mult_conversion_strippable_p (type, stmt)) rhs1 = rhs; else {