[(4/7)] Unsigned multiplies using wider signed multiplies

Submitted by Andrew Stubbs on July 14, 2011, 2:23 p.m.

Details

Message ID 4E1EFBE7.6040606@codesourcery.com
State New
Headers show

Commit Message

Andrew Stubbs July 14, 2011, 2:23 p.m.
On 12/07/11 15:07, Andrew Stubbs wrote:
> This update does the same thing as before, but updated for the changes
> earlier in the patch series. In particular, the build_and_insert_cast
> function and find_widening_optab_handler_and_mode changes have been
> moved up to patch 2.

And this update changes the way the casts are handled, partly because it 
got unwieldy towards the end of the patch series, and partly because I 
found a few bugs.

I've also ensured that it checks the precision of the types, rather than 
the mode size to ensure that it is bitfield safe.

OK?

Andrew

Comments

Richard Guenther July 14, 2011, 2:25 p.m.
On Thu, Jul 14, 2011 at 4:23 PM, Andrew Stubbs <ams@codesourcery.com> wrote:
> On 12/07/11 15:07, Andrew Stubbs wrote:
>>
>> This update does the same thing as before, but updated for the changes
>> earlier in the patch series. In particular, the build_and_insert_cast
>> function and find_widening_optab_handler_and_mode changes have been
>> moved up to patch 2.
>
> And this update changes the way the casts are handled, partly because it got
> unwieldy towards the end of the patch series, and partly because I found a
> few bugs.
>
> I've also ensured that it checks the precision of the types, rather than the
> mode size to ensure that it is bitfield safe.
>
> OK?

Ok.

Thanks,
Richard.

> Andrew
>

Patch hide | download patch | download mbox

2011-07-14  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.

--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/wmul-6.c
@@ -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" } } */
--- a/gcc/tree-ssa-math-opts.c
+++ b/gcc/tree-ssa-math-opts.c
@@ -2067,12 +2067,13 @@  is_widening_mult_p (gimple stmt,
 static bool
 convert_mult_to_widen (gimple stmt, gimple_stmt_iterator *gsi)
 {
-  tree lhs, rhs1, rhs2, type, type1, type2, tmp;
+  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);
@@ -2084,10 +2085,12 @@  convert_mult_to_widen (gimple stmt, gimple_stmt_iterator *gsi)
 
   to_mode = TYPE_MODE (type);
   from_mode = TYPE_MODE (type1);
+  from_unsigned1 = TYPE_UNSIGNED (type1);
+  from_unsigned2 = TYPE_UNSIGNED (type2);
 
-  if (TYPE_UNSIGNED (type1) && TYPE_UNSIGNED (type2))
+  if (from_unsigned1 && from_unsigned2)
     op = umul_widen_optab;
-  else if (!TYPE_UNSIGNED (type1) && !TYPE_UNSIGNED (type2))
+  else if (!from_unsigned1 && !from_unsigned2)
     op = smul_widen_optab;
   else
     op = usmul_widen_optab;
@@ -2096,22 +2099,45 @@  convert_mult_to_widen (gimple stmt, gimple_stmt_iterator *gsi)
 						  0, &actual_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,
+							  &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))
+  if (actual_precision != TYPE_PRECISION (type1)
+      || from_unsigned1 != TYPE_UNSIGNED (type1))
     {
       tmp = create_tmp_var (build_nonstandard_integer_type
-				(actual_precision, TYPE_UNSIGNED (type1)),
+				(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 (TYPE_UNSIGNED (type1) != TYPE_UNSIGNED (type2))
+      if (!tmp || from_unsigned1 != from_unsigned2)
 	tmp = create_tmp_var (build_nonstandard_integer_type
-				(actual_precision, TYPE_UNSIGNED (type2)),
+				(actual_precision, from_unsigned2),
 			      NULL);
       rhs2 = build_and_insert_cast (gsi, loc, tmp, rhs2);
     }
@@ -2136,7 +2162,7 @@  convert_plusminus_to_widen (gimple_stmt_iterator *gsi, gimple stmt,
 {
   gimple rhs1_stmt = NULL, rhs2_stmt = NULL;
   gimple conv1_stmt = NULL, conv2_stmt = NULL, conv_stmt;
-  tree type, type1, type2, tmp;
+  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;
@@ -2145,6 +2171,7 @@  convert_plusminus_to_widen (gimple_stmt_iterator *gsi, gimple stmt,
   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);
@@ -2238,9 +2265,21 @@  convert_plusminus_to_widen (gimple_stmt_iterator *gsi, gimple stmt,
 
   to_mode = TYPE_MODE (type);
   from_mode = TYPE_MODE (type1);
+  from_unsigned1 = TYPE_UNSIGNED (type1);
+  from_unsigned2 = TYPE_UNSIGNED (type2);
 
-  if (TYPE_UNSIGNED (type1) != TYPE_UNSIGNED (type2))
-    return false;
+  /* There's no such thing as a mixed sign madd yet, so use a wider mode.  */
+  if (from_unsigned1 != from_unsigned2)
+    {
+      enum machine_mode mode = GET_MODE_WIDER_MODE (from_mode);
+      if (GET_MODE_PRECISION (mode) < GET_MODE_PRECISION (to_mode))
+	{
+	  from_mode = mode;
+	  from_unsigned1 = from_unsigned2 = false;
+	}
+      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.
@@ -2248,6 +2287,7 @@  convert_plusminus_to_widen (gimple_stmt_iterator *gsi, gimple stmt,
      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);
@@ -2272,7 +2312,8 @@  convert_plusminus_to_widen (gimple_stmt_iterator *gsi, gimple stmt,
   /* 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);
+  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);
 
@@ -2282,13 +2323,21 @@  convert_plusminus_to_widen (gimple_stmt_iterator *gsi, gimple stmt,
   /* 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))
+  if (actual_precision != TYPE_PRECISION (type1)
+      || from_unsigned1 != TYPE_UNSIGNED (type1))
     {
       tmp = create_tmp_var (build_nonstandard_integer_type
-				(actual_precision, TYPE_UNSIGNED (type1)),
+				(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);
     }