Patchwork [(7/7)] Mixed-sign multiplies using narrowest mode

login
register
mail settings
Submitter Andrew Stubbs
Date June 23, 2011, 2:43 p.m.
Message ID <4E03511F.9040507@codesourcery.com>
Download mbox | patch
Permalink /patch/101641/
State New
Headers show

Comments

Andrew Stubbs - June 23, 2011, 2:43 p.m.
Patch 4 introduced support for using signed multiplies to code unsigned 
multiplies in a narrower mode. Patch 5 then introduced support for 
mis-matched input modes.

These two combined mean that there is case where only the smaller of two 
inputs is unsigned, and yet it still tries to user a mode wider than the 
larger, signed input. This is bad because it means unnecessary extends 
and because the wider operation might not exist.

This patch catches that case, and ensures that the smaller, unsigned 
input, is zero-extended to match the mode of the larger, signed input.

Of course, both inputs may still have to be extended to fit the nearest 
available instruction, so it doesn't make a difference every time.

OK?

Andrew

Patch

2011-06-23  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/smlalbb-3.c: New file.

--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/smlalbb-3.c
@@ -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" } } */
--- a/gcc/tree-ssa-math-opts.c
+++ b/gcc/tree-ssa-math-opts.c
@@ -2103,9 +2103,17 @@  convert_mult_to_widen (gimple stmt, gimple_stmt_iterator *gsi)
     {
       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;
+	  /* 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_UNSIGNED (type2)
+		  && TYPE_MODE (type2) == 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,
@@ -2244,14 +2252,21 @@  convert_plusminus_to_widen (gimple_stmt_iterator *gsi, gimple stmt,
   if (TYPE_UNSIGNED (type1) != TYPE_UNSIGNED (type2))
     {
       enum machine_mode mode = TYPE_MODE (type1);
-      mode = GET_MODE_WIDER_MODE (mode);
-      if (GET_MODE_SIZE (mode) < GET_MODE_SIZE (TYPE_MODE (type)))
+
+      /* 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_UNSIGNED (type2)
+	      && TYPE_MODE (type2) == mode))
 	{
-	  type1 = type2 = lang_hooks.types.type_for_mode (mode, 0);
-	  cast1 = cast2 = true;
+	  mode = GET_MODE_WIDER_MODE (mode);
+	  if (GET_MODE_SIZE (mode) >= GET_MODE_SIZE (TYPE_MODE (type)))
+	    return false;
 	}
-      else
-	return false;
+
+      type1 = type2 = lang_hooks.types.type_for_mode (mode, 0);
+      cast1 = cast2 = true;
     }
 
   if (TYPE_MODE (type2) != TYPE_MODE (type1))