2011-07-12 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.
@@ -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" } } */
@@ -2071,6 +2071,7 @@ convert_mult_to_widen (gimple stmt, gimple_stmt_iterator *gsi)
enum insn_code handler;
enum machine_mode to_mode, from_mode;
optab op;
+ bool do_cast = false;
lhs = gimple_assign_lhs (stmt);
type = TREE_TYPE (lhs);
@@ -2094,9 +2095,32 @@ convert_mult_to_widen (gimple stmt, gimple_stmt_iterator *gsi)
0, &from_mode);
if (handler == CODE_FOR_nothing)
- return false;
+ {
+ if (op != smul_widen_optab)
+ {
+ 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,
+ &from_mode);
- if (from_mode != TYPE_MODE (type1))
+ if (handler == CODE_FOR_nothing)
+ return false;
+
+ type1 = build_nonstandard_integer_type (
+ GET_MODE_PRECISION (from_mode),
+ 0);
+ type2 = type1;
+ do_cast = true;
+ }
+ else
+ return false;
+ }
+
+ if (from_mode != TYPE_MODE (type1) || do_cast)
{
location_t loc = gimple_location (stmt);
tree tmp1, tmp2;
@@ -2143,6 +2167,7 @@ convert_plusminus_to_widen (gimple_stmt_iterator *gsi, gimple stmt,
enum tree_code wmult_code;
enum insn_code handler;
enum machine_mode from_mode;
+ bool do_cast = false;
lhs = gimple_assign_lhs (stmt);
type = TREE_TYPE (lhs);
@@ -2234,8 +2259,21 @@ convert_plusminus_to_widen (gimple_stmt_iterator *gsi, gimple stmt,
else
return false;
+ /* We don't support usmadd yet, so try a wider signed mode. */
if (TYPE_UNSIGNED (type1) != TYPE_UNSIGNED (type2))
- return false;
+ {
+ enum machine_mode mode = TYPE_MODE (type1);
+ mode = GET_MODE_WIDER_MODE (mode);
+ if (GET_MODE_SIZE (mode) < GET_MODE_SIZE (TYPE_MODE (type)))
+ {
+ type1 = build_nonstandard_integer_type (GET_MODE_PRECISION (mode),
+ 0);
+ type2 = type1;
+ do_cast = true;
+ }
+ else
+ return false;
+ }
/* If there was a conversion between the multiply and addition
then we need to make sure it fits a multiply-and-accumulate.
@@ -2276,7 +2314,7 @@ convert_plusminus_to_widen (gimple_stmt_iterator *gsi, gimple stmt,
if (handler == CODE_FOR_nothing)
return false;
- if (TYPE_MODE (type1) != from_mode)
+ if (TYPE_MODE (type1) != from_mode || do_cast)
{
location_t loc = gimple_location (stmt);
tree tmp;