diff mbox

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

Message ID 4E09FFEF.6010603@codesourcery.com
State New
Headers show

Commit Message

Andrew Stubbs June 28, 2011, 4:23 p.m. UTC
On 23/06/11 15:43, Andrew Stubbs wrote:
> 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?

This update fixes Janis' issue with the testsuite.

Andrew
diff mbox

Patch

2011-06-24  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.

--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/wmul-9.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,
@@ -2227,14 +2235,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))