diff options
Diffstat (limited to 'toolchain-layer/recipes-devtools/gcc/gcc-4.6/linaro/gcc-4.6-linaro-r106796.patch')
-rw-r--r-- | toolchain-layer/recipes-devtools/gcc/gcc-4.6/linaro/gcc-4.6-linaro-r106796.patch | 1255 |
1 files changed, 1255 insertions, 0 deletions
diff --git a/toolchain-layer/recipes-devtools/gcc/gcc-4.6/linaro/gcc-4.6-linaro-r106796.patch b/toolchain-layer/recipes-devtools/gcc/gcc-4.6/linaro/gcc-4.6-linaro-r106796.patch new file mode 100644 index 0000000000..1a940975f3 --- /dev/null +++ b/toolchain-layer/recipes-devtools/gcc/gcc-4.6/linaro/gcc-4.6-linaro-r106796.patch @@ -0,0 +1,1255 @@ +2011-08-25 Andrew Stubbs <ams@codesourcery.com> + + Backport from FSF mainline: + + 2011-08-19 Andrew Stubbs <ams@codesourcery.com> + + gcc/ + * tree-ssa-math-opts.c (is_widening_mult_rhs_p): Handle constants + beyond conversions. + (convert_mult_to_widen): Convert constant inputs to the right type. + (convert_plusminus_to_widen): Don't automatically reject inputs that + are not an SSA_NAME. + Convert constant inputs to the right type. + + gcc/testsuite/ + * gcc.target/arm/wmul-11.c: New file. + * gcc.target/arm/wmul-12.c: New file. + * gcc.target/arm/wmul-13.c: New file. + + 2011-08-19 Andrew Stubbs <ams@codesourcery.com> + + gcc/ + * tree-ssa-math-opts.c (convert_plusminus_to_widen): Convert add_rhs + to the correct type. + + gcc/testsuite/ + * gcc.target/arm/wmul-10.c: New file. + + 2011-08-19 Andrew Stubbs <ams@codesourcery.com> + + gcc/ + * tree-ssa-math-opts.c (convert_mult_to_widen): Better handle + unsigned inputs of different modes. + (convert_plusminus_to_widen): Likewise. + + gcc/testsuite/ + * gcc.target/arm/wmul-9.c: New file. + * gcc.target/arm/wmul-bitfield-2.c: New file. + + 2011-08-19 Andrew Stubbs <ams@codesourcery.com> + + gcc/ + * tree-ssa-math-opts.c (is_widening_mult_rhs_p): Add new argument + 'type'. + Use 'type' from caller, not inferred from 'rhs'. + Don't reject non-conversion statements. Do return lhs in this case. + (is_widening_mult_p): Add new argument 'type'. + Use 'type' from caller, not inferred from 'stmt'. + Pass type to is_widening_mult_rhs_p. + (convert_mult_to_widen): Pass type to is_widening_mult_p. + (convert_plusminus_to_widen): Likewise. + + gcc/testsuite/ + * gcc.target/arm/wmul-8.c: New file. + + 2011-08-19 Andrew Stubbs <ams@codesourcery.com> + + gcc/ + * tree-ssa-math-opts.c (is_widening_mult_p): Remove FIXME. + Ensure the the larger type is the first operand. + + gcc/testsuite/ + * gcc.target/arm/wmul-7.c: New file. + + 2011-08-19 Andrew Stubbs <ams@codesourcery.com> + + gcc/ + * tree-ssa-math-opts.c (convert_mult_to_widen): Convert + unsupported unsigned multiplies to signed. + (convert_plusminus_to_widen): Likewise. + + gcc/testsuite/ + * gcc.target/arm/wmul-6.c: New file. + + 2011-08-19 Andrew Stubbs <ams@codesourcery.com> + + gcc/ + * tree-ssa-math-opts.c (convert_plusminus_to_widen): Permit a single + conversion statement separating multiply-and-accumulate. + + gcc/testsuite/ + * gcc.target/arm/wmul-5.c: New file. + * gcc.target/arm/no-wmla-1.c: New file. + + 2011-08-19 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_and_mode): 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 macro define. + (find_widening_optab_handler_and_mode): 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 (build_and_insert_cast): New function. + (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, and cast + input types to fit the new handler. + (convert_plusminus_to_widen): Likewise. + + gcc/testsuite/ + * gcc.target/arm/wmul-bitfield-1.c: New file. + + 2011-08-19 Andrew Stubbs <ams@codesourcery.com> + + gcc/ + * expr.c (expand_expr_real_2): Use widening_optab_handler. + * genopinit.c (optabs): Use set_widening_optab_handler for $N. + (gen_insn): $N now means $a must be wider than $b, not consecutive. + * optabs.c (widened_mode): New function. + (expand_widen_pattern_expr): Use widening_optab_handler. + (expand_binop_directly): Likewise. + (expand_binop): Likewise. + * optabs.h (widening_optab_handlers): New struct. + (optab_d): New member, 'widening'. + (widening_optab_handler): New function. + (set_widening_optab_handler): New function. + * tree-ssa-math-opts.c (convert_mult_to_widen): Use + widening_optab_handler. + (convert_plusminus_to_widen): Likewise. + +=== modified file 'gcc/config/arm/arm.md' +--- old/gcc/config/arm/arm.md 2011-08-13 08:32:32 +0000 ++++ new/gcc/config/arm/arm.md 2011-08-25 11:42:09 +0000 +@@ -1839,7 +1839,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 + +=== modified file 'gcc/expr.c' +--- old/gcc/expr.c 2011-08-13 08:32:32 +0000 ++++ new/gcc/expr.c 2011-08-25 11:42:09 +0000 +@@ -7688,18 +7688,16 @@ + { + 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 (optab_handler (this_optab, mode) != 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. */ +@@ -7714,10 +7712,10 @@ + 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 (optab_handler (this_optab, mode) != CODE_FOR_nothing) ++ if (find_widening_optab_handler (this_optab, mode, innermode, 0) ++ != CODE_FOR_nothing) + { + expand_operands (treeop0, treeop1, NULL_RTX, &op0, &op1, + EXPAND_NORMAL); +@@ -7725,7 +7723,8 @@ + unsignedp, this_optab); + return REDUCE_BIT_FIELD (temp); + } +- if (optab_handler (other_optab, mode) != CODE_FOR_nothing ++ if (find_widening_optab_handler (other_optab, mode, innermode, 0) ++ != CODE_FOR_nothing + && innermode == word_mode) + { + rtx htem, hipart; + +=== modified file 'gcc/genopinit.c' +--- old/gcc/genopinit.c 2011-05-05 15:43:06 +0000 ++++ new/gcc/genopinit.c 2011-07-15 13:06:31 +0000 +@@ -46,10 +46,12 @@ + used. $A and $B are replaced with the full name of the mode; $a and $b + are replaced with the short form of the name, as above. + +- If $N is present in the pattern, it means the two modes must be consecutive +- widths in the same mode class (e.g, QImode and HImode). $I means that +- only full integer modes should be considered for the next mode, and $F +- means that only float modes should be considered. ++ If $N is present in the pattern, it means the two modes must be in ++ the same mode class, and $b must be greater than $a (e.g, QImode ++ and HImode). ++ ++ $I means that only full integer modes should be considered for the ++ next mode, and $F means that only float modes should be considered. + $P means that both full and partial integer modes should be considered. + $Q means that only fixed-point modes should be considered. + +@@ -99,17 +101,17 @@ + "set_optab_handler (smulv_optab, $A, CODE_FOR_$(mulv$I$a3$))", + "set_optab_handler (umul_highpart_optab, $A, CODE_FOR_$(umul$a3_highpart$))", + "set_optab_handler (smul_highpart_optab, $A, CODE_FOR_$(smul$a3_highpart$))", +- "set_optab_handler (smul_widen_optab, $B, CODE_FOR_$(mul$a$b3$)$N)", +- "set_optab_handler (umul_widen_optab, $B, CODE_FOR_$(umul$a$b3$)$N)", +- "set_optab_handler (usmul_widen_optab, $B, CODE_FOR_$(usmul$a$b3$)$N)", +- "set_optab_handler (smadd_widen_optab, $B, CODE_FOR_$(madd$a$b4$)$N)", +- "set_optab_handler (umadd_widen_optab, $B, CODE_FOR_$(umadd$a$b4$)$N)", +- "set_optab_handler (ssmadd_widen_optab, $B, CODE_FOR_$(ssmadd$a$b4$)$N)", +- "set_optab_handler (usmadd_widen_optab, $B, CODE_FOR_$(usmadd$a$b4$)$N)", +- "set_optab_handler (smsub_widen_optab, $B, CODE_FOR_$(msub$a$b4$)$N)", +- "set_optab_handler (umsub_widen_optab, $B, CODE_FOR_$(umsub$a$b4$)$N)", +- "set_optab_handler (ssmsub_widen_optab, $B, CODE_FOR_$(ssmsub$a$b4$)$N)", +- "set_optab_handler (usmsub_widen_optab, $B, CODE_FOR_$(usmsub$a$b4$)$N)", ++ "set_widening_optab_handler (smul_widen_optab, $B, $A, CODE_FOR_$(mul$a$b3$)$N)", ++ "set_widening_optab_handler (umul_widen_optab, $B, $A, CODE_FOR_$(umul$a$b3$)$N)", ++ "set_widening_optab_handler (usmul_widen_optab, $B, $A, CODE_FOR_$(usmul$a$b3$)$N)", ++ "set_widening_optab_handler (smadd_widen_optab, $B, $A, CODE_FOR_$(madd$a$b4$)$N)", ++ "set_widening_optab_handler (umadd_widen_optab, $B, $A, CODE_FOR_$(umadd$a$b4$)$N)", ++ "set_widening_optab_handler (ssmadd_widen_optab, $B, $A, CODE_FOR_$(ssmadd$a$b4$)$N)", ++ "set_widening_optab_handler (usmadd_widen_optab, $B, $A, CODE_FOR_$(usmadd$a$b4$)$N)", ++ "set_widening_optab_handler (smsub_widen_optab, $B, $A, CODE_FOR_$(msub$a$b4$)$N)", ++ "set_widening_optab_handler (umsub_widen_optab, $B, $A, CODE_FOR_$(umsub$a$b4$)$N)", ++ "set_widening_optab_handler (ssmsub_widen_optab, $B, $A, CODE_FOR_$(ssmsub$a$b4$)$N)", ++ "set_widening_optab_handler (usmsub_widen_optab, $B, $A, CODE_FOR_$(usmsub$a$b4$)$N)", + "set_optab_handler (sdiv_optab, $A, CODE_FOR_$(div$a3$))", + "set_optab_handler (ssdiv_optab, $A, CODE_FOR_$(ssdiv$Q$a3$))", + "set_optab_handler (sdivv_optab, $A, CODE_FOR_$(div$V$I$a3$))", +@@ -304,7 +306,7 @@ + { + int force_float = 0, force_int = 0, force_partial_int = 0; + int force_fixed = 0; +- int force_consec = 0; ++ int force_wider = 0; + int matches = 1; + + for (pp = optabs[pindex]; pp[0] != '$' || pp[1] != '('; pp++) +@@ -322,7 +324,7 @@ + switch (*++pp) + { + case 'N': +- force_consec = 1; ++ force_wider = 1; + break; + case 'I': + force_int = 1; +@@ -391,7 +393,10 @@ + || mode_class[i] == MODE_VECTOR_FRACT + || mode_class[i] == MODE_VECTOR_UFRACT + || mode_class[i] == MODE_VECTOR_ACCUM +- || mode_class[i] == MODE_VECTOR_UACCUM)) ++ || mode_class[i] == MODE_VECTOR_UACCUM) ++ && (! force_wider ++ || *pp == 'a' ++ || m1 < i)) + break; + } + +@@ -411,8 +416,7 @@ + } + + if (matches && pp[0] == '$' && pp[1] == ')' +- && *np == 0 +- && (! force_consec || (int) GET_MODE_WIDER_MODE(m1) == m2)) ++ && *np == 0) + break; + } + + +=== modified file 'gcc/optabs.c' +--- old/gcc/optabs.c 2011-07-04 14:03:49 +0000 ++++ new/gcc/optabs.c 2011-08-11 15:46:01 +0000 +@@ -225,6 +225,61 @@ + return 1; + } + ++/* Given two input operands, OP0 and OP1, determine what the correct from_mode ++ for a widening operation would be. In most cases this would be OP0, but if ++ that's a constant it'll be VOIDmode, which isn't useful. */ ++ ++static enum machine_mode ++widened_mode (enum machine_mode to_mode, rtx op0, rtx op1) ++{ ++ enum machine_mode m0 = GET_MODE (op0); ++ enum machine_mode m1 = GET_MODE (op1); ++ enum machine_mode result; ++ ++ if (m0 == VOIDmode && m1 == VOIDmode) ++ return to_mode; ++ else if (m0 == VOIDmode || GET_MODE_SIZE (m0) < GET_MODE_SIZE (m1)) ++ result = m1; ++ else ++ result = m0; ++ ++ if (GET_MODE_SIZE (result) > GET_MODE_SIZE (to_mode)) ++ return to_mode; ++ ++ return result; ++} ++ ++/* 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_and_mode (optab op, enum machine_mode to_mode, ++ enum machine_mode from_mode, ++ int permit_non_widening, ++ enum machine_mode *found_mode) ++{ ++ 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) ++ { ++ if (found_mode) ++ *found_mode = from_mode; ++ 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 +@@ -517,8 +572,9 @@ + 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 = (int) optab_handler (widen_pattern_optab, +- TYPE_MODE (TREE_TYPE (ops->op2))); ++ icode = (int) find_widening_optab_handler (widen_pattern_optab, ++ TYPE_MODE (TREE_TYPE (ops->op2)), ++ tmode0, 0); + else + icode = (int) optab_handler (widen_pattern_optab, tmode0); + gcc_assert (icode != CODE_FOR_nothing); +@@ -1389,7 +1445,9 @@ + rtx target, int unsignedp, enum optab_methods methods, + rtx last) + { +- int icode = (int) optab_handler (binoptab, mode); ++ enum machine_mode from_mode = widened_mode (mode, op0, op1); ++ int icode = (int) find_widening_optab_handler (binoptab, mode, ++ from_mode, 1); + enum machine_mode mode0 = insn_data[icode].operand[1].mode; + enum machine_mode mode1 = insn_data[icode].operand[2].mode; + enum machine_mode tmp_mode; +@@ -1546,7 +1604,9 @@ + /* If we can do it with a three-operand insn, do so. */ + + if (methods != OPTAB_MUST_WIDEN +- && optab_handler (binoptab, mode) != CODE_FOR_nothing) ++ && find_widening_optab_handler (binoptab, mode, ++ widened_mode (mode, op0, op1), 1) ++ != CODE_FOR_nothing) + { + temp = expand_binop_directly (mode, binoptab, op0, op1, target, + unsignedp, methods, last); +@@ -1586,8 +1646,9 @@ + + if (binoptab == smul_optab + && GET_MODE_WIDER_MODE (mode) != VOIDmode +- && (optab_handler ((unsignedp ? umul_widen_optab : smul_widen_optab), +- GET_MODE_WIDER_MODE (mode)) ++ && (widening_optab_handler ((unsignedp ? umul_widen_optab ++ : smul_widen_optab), ++ GET_MODE_WIDER_MODE (mode), mode) + != CODE_FOR_nothing)) + { + temp = expand_binop (GET_MODE_WIDER_MODE (mode), +@@ -1618,9 +1679,11 @@ + if (optab_handler (binoptab, wider_mode) != CODE_FOR_nothing + || (binoptab == smul_optab + && GET_MODE_WIDER_MODE (wider_mode) != VOIDmode +- && (optab_handler ((unsignedp ? umul_widen_optab +- : smul_widen_optab), +- GET_MODE_WIDER_MODE (wider_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; +@@ -2043,8 +2106,8 @@ + && optab_handler (add_optab, word_mode) != CODE_FOR_nothing) + { + rtx product = NULL_RTX; +- +- if (optab_handler (umul_widen_optab, mode) != CODE_FOR_nothing) ++ if (widening_optab_handler (umul_widen_optab, mode, word_mode) ++ != CODE_FOR_nothing) + { + product = expand_doubleword_mult (mode, op0, op1, target, + true, methods); +@@ -2053,7 +2116,8 @@ + } + + if (product == NULL_RTX +- && optab_handler (smul_widen_optab, mode) != CODE_FOR_nothing) ++ && widening_optab_handler (smul_widen_optab, mode, word_mode) ++ != CODE_FOR_nothing) + { + product = expand_doubleword_mult (mode, op0, op1, target, + false, methods); +@@ -2144,7 +2208,8 @@ + wider_mode != VOIDmode; + wider_mode = GET_MODE_WIDER_MODE (wider_mode)) + { +- if (optab_handler (binoptab, wider_mode) != CODE_FOR_nothing ++ if (find_widening_optab_handler (binoptab, wider_mode, mode, 1) ++ != CODE_FOR_nothing + || (methods == OPTAB_LIB + && optab_libfunc (binoptab, wider_mode))) + { + +=== modified file 'gcc/optabs.h' +--- old/gcc/optabs.h 2011-05-05 15:43:06 +0000 ++++ new/gcc/optabs.h 2011-07-27 14:12:45 +0000 +@@ -42,6 +42,11 @@ + int insn_code; + }; + ++struct widening_optab_handlers ++{ ++ struct optab_handlers handlers[NUM_MACHINE_MODES][NUM_MACHINE_MODES]; ++}; ++ + struct optab_d + { + enum rtx_code code; +@@ -50,6 +55,7 @@ + void (*libcall_gen)(struct optab_d *, const char *name, char suffix, + enum machine_mode); + struct optab_handlers handlers[NUM_MACHINE_MODES]; ++ struct widening_optab_handlers *widening; + }; + typedef struct optab_d * optab; + +@@ -799,6 +805,15 @@ + extern void emit_unop_insn (int, rtx, rtx, enum rtx_code); + extern bool maybe_emit_unop_insn (int, rtx, rtx, enum rtx_code); + ++/* Find a widening optab even if it doesn't widen as much as we want. */ ++#define find_widening_optab_handler(A,B,C,D) \ ++ find_widening_optab_handler_and_mode (A, B, C, D, NULL) ++extern enum insn_code find_widening_optab_handler_and_mode (optab, ++ enum machine_mode, ++ enum machine_mode, ++ int, ++ enum machine_mode *); ++ + /* 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. */ +@@ -874,6 +889,23 @@ + + (int) CODE_FOR_nothing); + } + ++/* Like optab_handler, but for widening_operations that have a TO_MODE and ++ a FROM_MODE. */ ++ ++static inline enum insn_code ++widening_optab_handler (optab op, enum machine_mode to_mode, ++ enum machine_mode from_mode) ++{ ++ if (to_mode == from_mode || from_mode == VOIDmode) ++ return optab_handler (op, to_mode); ++ ++ if (op->widening) ++ return (enum insn_code) (op->widening->handlers[(int) to_mode][(int) from_mode].insn_code ++ + (int) CODE_FOR_nothing); ++ ++ return CODE_FOR_nothing; ++} ++ + /* Record that insn CODE should be used to implement mode MODE of OP. */ + + static inline void +@@ -882,6 +914,26 @@ + op->handlers[(int) mode].insn_code = (int) code - (int) CODE_FOR_nothing; + } + ++/* Like set_optab_handler, but for widening operations that have a TO_MODE ++ and a FROM_MODE. */ ++ ++static inline void ++set_widening_optab_handler (optab op, enum machine_mode to_mode, ++ enum machine_mode from_mode, enum insn_code code) ++{ ++ if (to_mode == from_mode) ++ set_optab_handler (op, to_mode, code); ++ else ++ { ++ if (op->widening == NULL) ++ op->widening = (struct widening_optab_handlers *) ++ xcalloc (1, sizeof (struct widening_optab_handlers)); ++ ++ op->widening->handlers[(int) to_mode][(int) from_mode].insn_code ++ = (int) code - (int) CODE_FOR_nothing; ++ } ++} ++ + /* Return the insn used to perform conversion OP from mode FROM_MODE + to mode TO_MODE; return CODE_FOR_nothing if the target does not have + such an insn. */ + +=== added file 'gcc/testsuite/gcc.target/arm/no-wmla-1.c' +--- old/gcc/testsuite/gcc.target/arm/no-wmla-1.c 1970-01-01 00:00:00 +0000 ++++ new/gcc/testsuite/gcc.target/arm/no-wmla-1.c 2011-07-15 13:52:38 +0000 +@@ -0,0 +1,11 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -march=armv7-a" } */ ++ ++int ++foo (int a, short b, short c) ++{ ++ int bc = b * c; ++ return a + (short)bc; ++} ++ ++/* { dg-final { scan-assembler "mul" } } */ + +=== added file 'gcc/testsuite/gcc.target/arm/wmul-10.c' +--- old/gcc/testsuite/gcc.target/arm/wmul-10.c 1970-01-01 00:00:00 +0000 ++++ new/gcc/testsuite/gcc.target/arm/wmul-10.c 2011-07-18 12:56:20 +0000 +@@ -0,0 +1,10 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -march=armv7-a" } */ ++ ++unsigned long long ++foo (unsigned short a, unsigned short *b, unsigned short *c) ++{ ++ return (unsigned)a + (unsigned long long)*b * (unsigned long long)*c; ++} ++ ++/* { dg-final { scan-assembler "umlal" } } */ + +=== added file 'gcc/testsuite/gcc.target/arm/wmul-11.c' +--- old/gcc/testsuite/gcc.target/arm/wmul-11.c 1970-01-01 00:00:00 +0000 ++++ new/gcc/testsuite/gcc.target/arm/wmul-11.c 2011-07-22 15:46:42 +0000 +@@ -0,0 +1,10 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -march=armv7-a" } */ ++ ++long long ++foo (int *b) ++{ ++ return 10 * (long long)*b; ++} ++ ++/* { dg-final { scan-assembler "smull" } } */ + +=== added file 'gcc/testsuite/gcc.target/arm/wmul-12.c' +--- old/gcc/testsuite/gcc.target/arm/wmul-12.c 1970-01-01 00:00:00 +0000 ++++ new/gcc/testsuite/gcc.target/arm/wmul-12.c 2011-07-22 15:46:42 +0000 +@@ -0,0 +1,11 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -march=armv7-a" } */ ++ ++long long ++foo (int *b, int *c) ++{ ++ int tmp = *b * *c; ++ return 10 + (long long)tmp; ++} ++ ++/* { dg-final { scan-assembler "smlal" } } */ + +=== added file 'gcc/testsuite/gcc.target/arm/wmul-13.c' +--- old/gcc/testsuite/gcc.target/arm/wmul-13.c 1970-01-01 00:00:00 +0000 ++++ new/gcc/testsuite/gcc.target/arm/wmul-13.c 2011-07-22 15:46:42 +0000 +@@ -0,0 +1,10 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -march=armv7-a" } */ ++ ++long long ++foo (int *a, int *b) ++{ ++ return *a + (long long)*b * 10; ++} ++ ++/* { dg-final { scan-assembler "smlal" } } */ + +=== added file 'gcc/testsuite/gcc.target/arm/wmul-5.c' +--- old/gcc/testsuite/gcc.target/arm/wmul-5.c 1970-01-01 00:00:00 +0000 ++++ new/gcc/testsuite/gcc.target/arm/wmul-5.c 2011-07-15 13:52:38 +0000 +@@ -0,0 +1,10 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -march=armv7-a" } */ ++ ++long long ++foo (long long a, char *b, char *c) ++{ ++ return a + *b * *c; ++} ++ ++/* { dg-final { scan-assembler "umlal" } } */ + +=== added file 'gcc/testsuite/gcc.target/arm/wmul-6.c' +--- old/gcc/testsuite/gcc.target/arm/wmul-6.c 1970-01-01 00:00:00 +0000 ++++ new/gcc/testsuite/gcc.target/arm/wmul-6.c 2011-07-15 13:59:11 +0000 +@@ -0,0 +1,10 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -march=armv7-a" } */ ++ ++long long ++foo (long long a, unsigned char *b, signed char *c) ++{ ++ return a + (long long)*b * (long long)*c; ++} ++ ++/* { dg-final { scan-assembler "smlal" } } */ + +=== added file 'gcc/testsuite/gcc.target/arm/wmul-7.c' +--- old/gcc/testsuite/gcc.target/arm/wmul-7.c 1970-01-01 00:00:00 +0000 ++++ new/gcc/testsuite/gcc.target/arm/wmul-7.c 2011-07-15 14:11:23 +0000 +@@ -0,0 +1,10 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -march=armv7-a" } */ ++ ++unsigned long long ++foo (unsigned long long a, unsigned char *b, unsigned short *c) ++{ ++ return a + *b * *c; ++} ++ ++/* { dg-final { scan-assembler "umlal" } } */ + +=== added file 'gcc/testsuite/gcc.target/arm/wmul-8.c' +--- old/gcc/testsuite/gcc.target/arm/wmul-8.c 1970-01-01 00:00:00 +0000 ++++ new/gcc/testsuite/gcc.target/arm/wmul-8.c 2011-07-15 14:16:54 +0000 +@@ -0,0 +1,10 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -march=armv7-a" } */ ++ ++long long ++foo (long long a, int *b, int *c) ++{ ++ return a + *b * *c; ++} ++ ++/* { dg-final { scan-assembler "smlal" } } */ + +=== added file 'gcc/testsuite/gcc.target/arm/wmul-9.c' +--- old/gcc/testsuite/gcc.target/arm/wmul-9.c 1970-01-01 00:00:00 +0000 ++++ new/gcc/testsuite/gcc.target/arm/wmul-9.c 2011-07-15 14:22:39 +0000 +@@ -0,0 +1,10 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -march=armv7-a" } */ ++ ++long long ++foo (long long a, short *b, char *c) ++{ ++ return a + *b * *c; ++} ++ ++/* { dg-final { scan-assembler "smlalbb" } } */ + +=== added file 'gcc/testsuite/gcc.target/arm/wmul-bitfield-1.c' +--- old/gcc/testsuite/gcc.target/arm/wmul-bitfield-1.c 1970-01-01 00:00:00 +0000 ++++ new/gcc/testsuite/gcc.target/arm/wmul-bitfield-1.c 2011-07-15 13:44:50 +0000 +@@ -0,0 +1,17 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -march=armv7-a" } */ ++ ++struct bf ++{ ++ int a : 3; ++ int b : 15; ++ int c : 3; ++}; ++ ++long long ++foo (long long a, struct bf b, struct bf c) ++{ ++ return a + b.b * c.b; ++} ++ ++/* { dg-final { scan-assembler "smlalbb" } } */ + +=== added file 'gcc/testsuite/gcc.target/arm/wmul-bitfield-2.c' +--- old/gcc/testsuite/gcc.target/arm/wmul-bitfield-2.c 1970-01-01 00:00:00 +0000 ++++ new/gcc/testsuite/gcc.target/arm/wmul-bitfield-2.c 2011-07-15 14:22:39 +0000 +@@ -0,0 +1,17 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -march=armv7-a" } */ ++ ++struct bf ++{ ++ int a : 3; ++ unsigned int b : 15; ++ int c : 3; ++}; ++ ++long long ++foo (long long a, struct bf b, struct bf c) ++{ ++ return a + b.b * c.c; ++} ++ ++/* { dg-final { scan-assembler "smlalbb" } } */ + +=== modified file 'gcc/tree-cfg.c' +--- old/gcc/tree-cfg.c 2011-07-01 09:19:21 +0000 ++++ new/gcc/tree-cfg.c 2011-07-15 13:44:50 +0000 +@@ -3574,7 +3574,7 @@ + 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: +@@ -3667,7 +3667,7 @@ + && !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"); + +=== modified file 'gcc/tree-ssa-math-opts.c' +--- old/gcc/tree-ssa-math-opts.c 2011-03-11 16:36:16 +0000 ++++ new/gcc/tree-ssa-math-opts.c 2011-08-09 10:26:48 +0000 +@@ -1266,39 +1266,67 @@ + } + }; + +-/* Return true if RHS is a suitable operand for a widening multiplication. ++/* Build a gimple assignment to cast VAL to TARGET. Insert the statement ++ prior to GSI's current position, and return the fresh SSA name. */ ++ ++static tree ++build_and_insert_cast (gimple_stmt_iterator *gsi, location_t loc, ++ tree target, tree val) ++{ ++ tree result = make_ssa_name (target, NULL); ++ gimple stmt = gimple_build_assign_with_ops (CONVERT_EXPR, result, val, NULL); ++ gimple_set_location (stmt, loc); ++ gsi_insert_before (gsi, stmt, GSI_SAME_STMT); ++ return result; ++} ++ ++/* Return true if RHS is a suitable operand for a widening multiplication, ++ assuming a target type of TYPE. + 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. */ + + static bool +-is_widening_mult_rhs_p (tree rhs, tree *type_out, tree *new_rhs_out) ++is_widening_mult_rhs_p (tree type, tree rhs, tree *type_out, ++ tree *new_rhs_out) + { + gimple stmt; +- tree type, type1, rhs1; ++ tree type1, rhs1; + enum tree_code rhs_code; + + if (TREE_CODE (rhs) == SSA_NAME) + { +- type = TREE_TYPE (rhs); + stmt = SSA_NAME_DEF_STMT (rhs); +- if (!is_gimple_assign (stmt)) +- return false; +- +- rhs_code = gimple_assign_rhs_code (stmt); +- if (TREE_CODE (type) == INTEGER_TYPE +- ? !CONVERT_EXPR_CODE_P (rhs_code) +- : rhs_code != FIXED_CONVERT_EXPR) +- return false; +- +- rhs1 = gimple_assign_rhs1 (stmt); ++ 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) ++ rhs1 = rhs; ++ else ++ { ++ rhs1 = gimple_assign_rhs1 (stmt); ++ ++ if (TREE_CODE (rhs1) == INTEGER_CST) ++ { ++ *new_rhs_out = rhs1; ++ *type_out = NULL; ++ return true; ++ } ++ } ++ } ++ else ++ rhs1 = rhs; ++ + 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; +@@ -1316,28 +1344,27 @@ + return false; + } + +-/* Return true if STMT performs a widening multiplication. If so, +- store the unwidened types of the operands in *TYPE1_OUT and *TYPE2_OUT +- respectively. Also fill *RHS1_OUT and *RHS2_OUT such that converting +- those operands to types *TYPE1_OUT and *TYPE2_OUT would give the +- operands of the multiplication. */ ++/* Return true if STMT performs a widening multiplication, assuming the ++ output type is TYPE. If so, store the unwidened types of the operands ++ in *TYPE1_OUT and *TYPE2_OUT respectively. Also fill *RHS1_OUT and ++ *RHS2_OUT such that converting those operands to types *TYPE1_OUT ++ and *TYPE2_OUT would give the operands of the multiplication. */ + + static bool +-is_widening_mult_p (gimple stmt, ++is_widening_mult_p (tree type, gimple stmt, + tree *type1_out, tree *rhs1_out, + tree *type2_out, tree *rhs2_out) + { +- tree type; +- +- type = TREE_TYPE (gimple_assign_lhs (stmt)); + if (TREE_CODE (type) != INTEGER_TYPE + && TREE_CODE (type) != FIXED_POINT_TYPE) + return false; + +- if (!is_widening_mult_rhs_p (gimple_assign_rhs1 (stmt), type1_out, rhs1_out)) ++ if (!is_widening_mult_rhs_p (type, gimple_assign_rhs1 (stmt), type1_out, ++ rhs1_out)) + return false; + +- if (!is_widening_mult_rhs_p (gimple_assign_rhs2 (stmt), type2_out, rhs2_out)) ++ if (!is_widening_mult_rhs_p (type, gimple_assign_rhs2 (stmt), type2_out, ++ rhs2_out)) + return false; + + if (*type1_out == NULL) +@@ -1354,6 +1381,18 @@ + *type2_out = *type1_out; + } + ++ /* Ensure that the larger of the two operands comes first. */ ++ if (TYPE_PRECISION (*type1_out) < TYPE_PRECISION (*type2_out)) ++ { ++ tree tmp; ++ tmp = *type1_out; ++ *type1_out = *type2_out; ++ *type2_out = tmp; ++ tmp = *rhs1_out; ++ *rhs1_out = *rhs2_out; ++ *rhs2_out = tmp; ++ } ++ + return true; + } + +@@ -1362,31 +1401,100 @@ + value is true iff we converted the statement. */ + + static bool +-convert_mult_to_widen (gimple stmt) ++convert_mult_to_widen (gimple stmt, gimple_stmt_iterator *gsi) + { +- tree lhs, rhs1, rhs2, type, type1, type2; ++ tree lhs, rhs1, rhs2, type, type1, type2, tmp = NULL; + enum insn_code handler; ++ enum machine_mode to_mode, from_mode, actual_mode; ++ optab op; ++ int actual_precision; ++ location_t loc = gimple_location (stmt); ++ bool from_unsigned1, from_unsigned2; + + lhs = gimple_assign_lhs (stmt); + type = TREE_TYPE (lhs); + if (TREE_CODE (type) != INTEGER_TYPE) + return false; + +- if (!is_widening_mult_p (stmt, &type1, &rhs1, &type2, &rhs2)) ++ if (!is_widening_mult_p (type, stmt, &type1, &rhs1, &type2, &rhs2)) + return false; + +- if (TYPE_UNSIGNED (type1) && TYPE_UNSIGNED (type2)) +- handler = optab_handler (umul_widen_optab, TYPE_MODE (type)); +- else if (!TYPE_UNSIGNED (type1) && !TYPE_UNSIGNED (type2)) +- handler = optab_handler (smul_widen_optab, TYPE_MODE (type)); ++ to_mode = TYPE_MODE (type); ++ from_mode = TYPE_MODE (type1); ++ from_unsigned1 = TYPE_UNSIGNED (type1); ++ from_unsigned2 = TYPE_UNSIGNED (type2); ++ ++ if (from_unsigned1 && from_unsigned2) ++ op = umul_widen_optab; ++ else if (!from_unsigned1 && !from_unsigned2) ++ op = smul_widen_optab; + else +- handler = optab_handler (usmul_widen_optab, TYPE_MODE (type)); ++ op = usmul_widen_optab; ++ ++ handler = find_widening_optab_handler_and_mode (op, to_mode, from_mode, ++ 0, &actual_mode); + + if (handler == CODE_FOR_nothing) +- return false; +- +- gimple_assign_set_rhs1 (stmt, fold_convert (type1, rhs1)); +- gimple_assign_set_rhs2 (stmt, fold_convert (type2, rhs2)); ++ { ++ if (op != smul_widen_optab) ++ { ++ /* We can use a signed multiply with unsigned types as long as ++ there is a wider mode to use, or it is the smaller of the two ++ types that is unsigned. Note that type1 >= type2, always. */ ++ if ((TYPE_UNSIGNED (type1) ++ && TYPE_PRECISION (type1) == GET_MODE_PRECISION (from_mode)) ++ || (TYPE_UNSIGNED (type2) ++ && TYPE_PRECISION (type2) == GET_MODE_PRECISION (from_mode))) ++ { ++ from_mode = GET_MODE_WIDER_MODE (from_mode); ++ if (GET_MODE_SIZE (to_mode) <= GET_MODE_SIZE (from_mode)) ++ return false; ++ } ++ ++ op = smul_widen_optab; ++ handler = find_widening_optab_handler_and_mode (op, to_mode, ++ from_mode, 0, ++ &actual_mode); ++ ++ if (handler == CODE_FOR_nothing) ++ return false; ++ ++ from_unsigned1 = from_unsigned2 = false; ++ } ++ else ++ return false; ++ } ++ ++ /* Ensure that the inputs to the handler are in the correct precison ++ for the opcode. This will be the full mode size. */ ++ actual_precision = GET_MODE_PRECISION (actual_mode); ++ if (actual_precision != TYPE_PRECISION (type1) ++ || from_unsigned1 != TYPE_UNSIGNED (type1)) ++ { ++ tmp = create_tmp_var (build_nonstandard_integer_type ++ (actual_precision, from_unsigned1), ++ NULL); ++ rhs1 = build_and_insert_cast (gsi, loc, tmp, rhs1); ++ } ++ if (actual_precision != TYPE_PRECISION (type2) ++ || from_unsigned2 != TYPE_UNSIGNED (type2)) ++ { ++ /* Reuse the same type info, if possible. */ ++ if (!tmp || from_unsigned1 != from_unsigned2) ++ tmp = create_tmp_var (build_nonstandard_integer_type ++ (actual_precision, from_unsigned2), ++ NULL); ++ rhs2 = build_and_insert_cast (gsi, loc, tmp, rhs2); ++ } ++ ++ /* Handle constants. */ ++ if (TREE_CODE (rhs1) == INTEGER_CST) ++ rhs1 = fold_convert (type1, rhs1); ++ if (TREE_CODE (rhs2) == INTEGER_CST) ++ rhs2 = fold_convert (type2, rhs2); ++ ++ gimple_assign_set_rhs1 (stmt, rhs1); ++ gimple_assign_set_rhs2 (stmt, rhs2); + gimple_assign_set_rhs_code (stmt, WIDEN_MULT_EXPR); + update_stmt (stmt); + return true; +@@ -1403,11 +1511,17 @@ + enum tree_code code) + { + gimple rhs1_stmt = NULL, rhs2_stmt = NULL; +- tree type, type1, type2; ++ gimple conv1_stmt = NULL, conv2_stmt = NULL, conv_stmt; ++ tree type, type1, type2, optype, tmp = NULL; + tree lhs, rhs1, rhs2, mult_rhs1, mult_rhs2, add_rhs; + enum tree_code rhs1_code = ERROR_MARK, rhs2_code = ERROR_MARK; + optab this_optab; + enum tree_code wmult_code; ++ enum insn_code handler; ++ enum machine_mode to_mode, from_mode, actual_mode; ++ location_t loc = gimple_location (stmt); ++ int actual_precision; ++ bool from_unsigned1, from_unsigned2; + + lhs = gimple_assign_lhs (stmt); + type = TREE_TYPE (lhs); +@@ -1429,8 +1543,6 @@ + if (is_gimple_assign (rhs1_stmt)) + rhs1_code = gimple_assign_rhs_code (rhs1_stmt); + } +- else +- return false; + + if (TREE_CODE (rhs2) == SSA_NAME) + { +@@ -1438,57 +1550,160 @@ + if (is_gimple_assign (rhs2_stmt)) + rhs2_code = gimple_assign_rhs_code (rhs2_stmt); + } +- else +- return false; +- +- if (code == PLUS_EXPR && rhs1_code == MULT_EXPR) +- { +- if (!is_widening_mult_p (rhs1_stmt, &type1, &mult_rhs1, +- &type2, &mult_rhs2)) +- return false; +- add_rhs = rhs2; +- } +- else if (rhs2_code == MULT_EXPR) +- { +- if (!is_widening_mult_p (rhs2_stmt, &type1, &mult_rhs1, +- &type2, &mult_rhs2)) +- return false; +- add_rhs = rhs1; +- } +- else if (code == PLUS_EXPR && rhs1_code == WIDEN_MULT_EXPR) +- { +- mult_rhs1 = gimple_assign_rhs1 (rhs1_stmt); +- mult_rhs2 = gimple_assign_rhs2 (rhs1_stmt); +- type1 = TREE_TYPE (mult_rhs1); +- type2 = TREE_TYPE (mult_rhs2); +- add_rhs = rhs2; +- } +- else if (rhs2_code == WIDEN_MULT_EXPR) +- { +- mult_rhs1 = gimple_assign_rhs1 (rhs2_stmt); +- mult_rhs2 = gimple_assign_rhs2 (rhs2_stmt); +- type1 = TREE_TYPE (mult_rhs1); +- type2 = TREE_TYPE (mult_rhs2); +- add_rhs = rhs1; +- } +- else +- return false; +- +- if (TYPE_UNSIGNED (type1) != TYPE_UNSIGNED (type2)) +- return false; ++ ++ /* Allow for one conversion statement between the multiply ++ and addition/subtraction statement. If there are more than ++ one conversions then we assume they would invalidate this ++ transformation. If that's not the case then they should have ++ been folded before now. */ ++ if (CONVERT_EXPR_CODE_P (rhs1_code)) ++ { ++ conv1_stmt = rhs1_stmt; ++ rhs1 = gimple_assign_rhs1 (rhs1_stmt); ++ if (TREE_CODE (rhs1) == SSA_NAME) ++ { ++ rhs1_stmt = SSA_NAME_DEF_STMT (rhs1); ++ if (is_gimple_assign (rhs1_stmt)) ++ rhs1_code = gimple_assign_rhs_code (rhs1_stmt); ++ } ++ else ++ return false; ++ } ++ if (CONVERT_EXPR_CODE_P (rhs2_code)) ++ { ++ conv2_stmt = rhs2_stmt; ++ rhs2 = gimple_assign_rhs1 (rhs2_stmt); ++ if (TREE_CODE (rhs2) == SSA_NAME) ++ { ++ rhs2_stmt = SSA_NAME_DEF_STMT (rhs2); ++ if (is_gimple_assign (rhs2_stmt)) ++ rhs2_code = gimple_assign_rhs_code (rhs2_stmt); ++ } ++ else ++ return false; ++ } ++ ++ /* If code is WIDEN_MULT_EXPR then it would seem unnecessary to call ++ is_widening_mult_p, but we still need the rhs returns. ++ ++ It might also appear that it would be sufficient to use the existing ++ operands of the widening multiply, but that would limit the choice of ++ multiply-and-accumulate instructions. */ ++ if (code == PLUS_EXPR ++ && (rhs1_code == MULT_EXPR || rhs1_code == WIDEN_MULT_EXPR)) ++ { ++ if (!is_widening_mult_p (type, rhs1_stmt, &type1, &mult_rhs1, ++ &type2, &mult_rhs2)) ++ return false; ++ add_rhs = rhs2; ++ conv_stmt = conv1_stmt; ++ } ++ else if (rhs2_code == MULT_EXPR || rhs2_code == WIDEN_MULT_EXPR) ++ { ++ if (!is_widening_mult_p (type, rhs2_stmt, &type1, &mult_rhs1, ++ &type2, &mult_rhs2)) ++ return false; ++ add_rhs = rhs1; ++ conv_stmt = conv2_stmt; ++ } ++ else ++ return false; ++ ++ to_mode = TYPE_MODE (type); ++ from_mode = TYPE_MODE (type1); ++ from_unsigned1 = TYPE_UNSIGNED (type1); ++ from_unsigned2 = TYPE_UNSIGNED (type2); ++ ++ /* There's no such thing as a mixed sign madd yet, so use a wider mode. */ ++ if (from_unsigned1 != from_unsigned2) ++ { ++ /* We can use a signed multiply with unsigned types as long as ++ there is a wider mode to use, or it is the smaller of the two ++ types that is unsigned. Note that type1 >= type2, always. */ ++ if ((from_unsigned1 ++ && TYPE_PRECISION (type1) == GET_MODE_PRECISION (from_mode)) ++ || (from_unsigned2 ++ && TYPE_PRECISION (type2) == GET_MODE_PRECISION (from_mode))) ++ { ++ from_mode = GET_MODE_WIDER_MODE (from_mode); ++ if (GET_MODE_SIZE (from_mode) >= GET_MODE_SIZE (to_mode)) ++ return false; ++ } ++ ++ from_unsigned1 = from_unsigned2 = false; ++ } ++ ++ /* If there was a conversion between the multiply and addition ++ then we need to make sure it fits a multiply-and-accumulate. ++ The should be a single mode change which does not change the ++ value. */ ++ if (conv_stmt) ++ { ++ /* We use the original, unmodified data types for this. */ ++ tree from_type = TREE_TYPE (gimple_assign_rhs1 (conv_stmt)); ++ tree to_type = TREE_TYPE (gimple_assign_lhs (conv_stmt)); ++ int data_size = TYPE_PRECISION (type1) + TYPE_PRECISION (type2); ++ bool is_unsigned = TYPE_UNSIGNED (type1) && TYPE_UNSIGNED (type2); ++ ++ if (TYPE_PRECISION (from_type) > TYPE_PRECISION (to_type)) ++ { ++ /* Conversion is a truncate. */ ++ if (TYPE_PRECISION (to_type) < data_size) ++ return false; ++ } ++ else if (TYPE_PRECISION (from_type) < TYPE_PRECISION (to_type)) ++ { ++ /* Conversion is an extend. Check it's the right sort. */ ++ if (TYPE_UNSIGNED (from_type) != is_unsigned ++ && !(is_unsigned && TYPE_PRECISION (from_type) > data_size)) ++ return false; ++ } ++ /* else convert is a no-op for our purposes. */ ++ } + + /* Verify that the machine can perform a widening multiply + 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 (optab_handler (this_optab, TYPE_MODE (type)) == CODE_FOR_nothing) ++ optype = build_nonstandard_integer_type (from_mode, from_unsigned1); ++ this_optab = optab_for_tree_code (wmult_code, optype, optab_default); ++ handler = find_widening_optab_handler_and_mode (this_optab, to_mode, ++ from_mode, 0, &actual_mode); ++ ++ if (handler == CODE_FOR_nothing) + return false; + +- /* ??? May need some type verification here? */ +- +- gimple_assign_set_rhs_with_ops_1 (gsi, wmult_code, +- fold_convert (type1, mult_rhs1), +- fold_convert (type2, mult_rhs2), ++ /* Ensure that the inputs to the handler are in the correct precison ++ for the opcode. This will be the full mode size. */ ++ actual_precision = GET_MODE_PRECISION (actual_mode); ++ if (actual_precision != TYPE_PRECISION (type1) ++ || from_unsigned1 != TYPE_UNSIGNED (type1)) ++ { ++ tmp = create_tmp_var (build_nonstandard_integer_type ++ (actual_precision, from_unsigned1), ++ NULL); ++ mult_rhs1 = build_and_insert_cast (gsi, loc, tmp, mult_rhs1); ++ } ++ if (actual_precision != TYPE_PRECISION (type2) ++ || from_unsigned2 != TYPE_UNSIGNED (type2)) ++ { ++ if (!tmp || from_unsigned1 != from_unsigned2) ++ tmp = create_tmp_var (build_nonstandard_integer_type ++ (actual_precision, from_unsigned2), ++ NULL); ++ mult_rhs2 = build_and_insert_cast (gsi, loc, tmp, mult_rhs2); ++ } ++ ++ if (!useless_type_conversion_p (type, TREE_TYPE (add_rhs))) ++ add_rhs = build_and_insert_cast (gsi, loc, create_tmp_var (type, NULL), ++ add_rhs); ++ ++ /* Handle constants. */ ++ if (TREE_CODE (mult_rhs1) == INTEGER_CST) ++ rhs1 = fold_convert (type1, mult_rhs1); ++ if (TREE_CODE (mult_rhs2) == INTEGER_CST) ++ rhs2 = fold_convert (type2, mult_rhs2); ++ ++ gimple_assign_set_rhs_with_ops_1 (gsi, wmult_code, mult_rhs1, mult_rhs2, + add_rhs); + update_stmt (gsi_stmt (*gsi)); + return true; +@@ -1696,7 +1911,7 @@ + switch (code) + { + case MULT_EXPR: +- if (!convert_mult_to_widen (stmt) ++ if (!convert_mult_to_widen (stmt, &gsi) + && convert_mult_to_fma (stmt, + gimple_assign_rhs1 (stmt), + gimple_assign_rhs2 (stmt))) + |