Hi Marc,

> >>> +     (nop:type (op (convert:ty1 @1) (convert:ty2 @2)))))
> >>
> >> Please don't use 'nop' directly, use 'convert' instead. This line is very
> >> suspicious, both arguments of op should have the same type. Specifying the
> >> outertype should be unnecessary, it is always 'type'. And if necessary, I
> >> expect '(convert:ty1 @1)' is the same as '{ arg0; }'.
> >>
> >
> > Ah I wasn't aware I could use arg0 here. I've updated the patch, though I 
> > don't
> > really find this clearer.
> 
> > + (convert (op (convert:ty1 { arg0; }) (convert:ty2 { arg1; })))))
> 
> I think you misunderstood my point. What you wrote is equivalent to:
> 
>       (convert (op { arg0; } { arg1; }))))
> 
> since arg0 already has type ty1. And I am complaining that both arguments 
> to op must have the same type, but you are creating one of type ty1 and 
> one of type ty2, which doesn't clearly indicate that ty1==ty2.
>

Ah ok, I've reverted the previous changes and added a types_match on ty1 and 
ty2.
 
> Maybe experiment with
> (long double)some_float * (long double)some_double
> cast to either float or double.
> 

I did, and none of them were an issue:

All of these worked fine and did the operation as expected as DFmode
now, whereas before all except the first would have used TFmode.

double foo (float a, double b)
{
  return (long double)a * (long double)b;
}

double bar (float a, double b)
{
  float x = (long double)a;
  return x * (long double)b;
}

void foo_ (double b, double *c)
{
  float a = (double)3.0d;
  long double e = (long double)a * (long double)b;
  *c = (double)e;
}

> SCALAR_FLOAT_TYPE_P may be safer than FLOAT_TYPE_P.

Hmm, I can't think of any reason why these rules shouldn't apply to vector or
complex float modes.  The old code in convert.c already used FLOAT_TYPE_P.

I've attached the updated patch.

Thanks,
Tamar

> 
> -- 
> Marc Glisse

-- 
diff --git a/gcc/convert.c b/gcc/convert.c
index 1a3353c870768a33fe22480ec97c7d3e0c504075..a16b7af0ec54693eb4f1e3a110aabc1aa18eb8df 100644
--- a/gcc/convert.c
+++ b/gcc/convert.c
@@ -295,92 +295,6 @@ convert_to_real_1 (tree type, tree expr, bool fold_p)
 	      return build1 (TREE_CODE (expr), type, arg);
 	    }
 	  break;
-	/* Convert (outertype)((innertype0)a+(innertype1)b)
-	   into ((newtype)a+(newtype)b) where newtype
-	   is the widest mode from all of these.  */
-	case PLUS_EXPR:
-	case MINUS_EXPR:
-	case MULT_EXPR:
-	case RDIV_EXPR:
-	   {
-	     tree arg0 = strip_float_extensions (TREE_OPERAND (expr, 0));
-	     tree arg1 = strip_float_extensions (TREE_OPERAND (expr, 1));
-
-	     if (FLOAT_TYPE_P (TREE_TYPE (arg0))
-		 && FLOAT_TYPE_P (TREE_TYPE (arg1))
-		 && DECIMAL_FLOAT_TYPE_P (itype) == DECIMAL_FLOAT_TYPE_P (type))
-	       {
-		  tree newtype = type;
-
-		  if (TYPE_MODE (TREE_TYPE (arg0)) == SDmode
-		      || TYPE_MODE (TREE_TYPE (arg1)) == SDmode
-		      || TYPE_MODE (type) == SDmode)
-		    newtype = dfloat32_type_node;
-		  if (TYPE_MODE (TREE_TYPE (arg0)) == DDmode
-		      || TYPE_MODE (TREE_TYPE (arg1)) == DDmode
-		      || TYPE_MODE (type) == DDmode)
-		    newtype = dfloat64_type_node;
-		  if (TYPE_MODE (TREE_TYPE (arg0)) == TDmode
-		      || TYPE_MODE (TREE_TYPE (arg1)) == TDmode
-		      || TYPE_MODE (type) == TDmode)
-                    newtype = dfloat128_type_node;
-		  if (newtype == dfloat32_type_node
-		      || newtype == dfloat64_type_node
-		      || newtype == dfloat128_type_node)
-		    {
-		      expr = build2 (TREE_CODE (expr), newtype,
-				     convert_to_real_1 (newtype, arg0,
-							fold_p),
-				     convert_to_real_1 (newtype, arg1,
-							fold_p));
-		      if (newtype == type)
-			return expr;
-		      break;
-		    }
-
-		  if (TYPE_PRECISION (TREE_TYPE (arg0)) > TYPE_PRECISION (newtype))
-		    newtype = TREE_TYPE (arg0);
-		  if (TYPE_PRECISION (TREE_TYPE (arg1)) > TYPE_PRECISION (newtype))
-		    newtype = TREE_TYPE (arg1);
-		  /* Sometimes this transformation is safe (cannot
-		     change results through affecting double rounding
-		     cases) and sometimes it is not.  If NEWTYPE is
-		     wider than TYPE, e.g. (float)((long double)double
-		     + (long double)double) converted to
-		     (float)(double + double), the transformation is
-		     unsafe regardless of the details of the types
-		     involved; double rounding can arise if the result
-		     of NEWTYPE arithmetic is a NEWTYPE value half way
-		     between two representable TYPE values but the
-		     exact value is sufficiently different (in the
-		     right direction) for this difference to be
-		     visible in ITYPE arithmetic.  If NEWTYPE is the
-		     same as TYPE, however, the transformation may be
-		     safe depending on the types involved: it is safe
-		     if the ITYPE has strictly more than twice as many
-		     mantissa bits as TYPE, can represent infinities
-		     and NaNs if the TYPE can, and has sufficient
-		     exponent range for the product or ratio of two
-		     values representable in the TYPE to be within the
-		     range of normal values of ITYPE.  */
-		  if (TYPE_PRECISION (newtype) < TYPE_PRECISION (itype)
-		      && (flag_unsafe_math_optimizations
-			  || (TYPE_PRECISION (newtype) == TYPE_PRECISION (type)
-			      && real_can_shorten_arithmetic (TYPE_MODE (itype),
-							      TYPE_MODE (type))
-			      && !excess_precision_type (newtype))))
-		    {
-		      expr = build2 (TREE_CODE (expr), newtype,
-				     convert_to_real_1 (newtype, arg0,
-							fold_p),
-				     convert_to_real_1 (newtype, arg1,
-							fold_p));
-		      if (newtype == type)
-			return expr;
-		    }
-	       }
-	   }
-	  break;
 	default:
 	  break;
       }
diff --git a/gcc/match.pd b/gcc/match.pd
index 97a94cd8b2f2e0fee9ffbc76c5277c97689b6f42..974a730fdef30cc79bdd45610d2cfd1c42a7623a 100644
--- a/gcc/match.pd
+++ b/gcc/match.pd
@@ -4758,37 +4758,116 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
    the C/C++ front-ends by shorten_binary_op and shorten_compare.  Long
    term we want to move all that code out of the front-ends into here.  */
 
-/* If we have a narrowing conversion of an arithmetic operation where
-   both operands are widening conversions from the same type as the outer
-   narrowing conversion.  Then convert the innermost operands to a suitable
-   unsigned type (to avoid introducing undefined behavior), perform the
-   operation and convert the result to the desired type.  */
-(for op (plus minus)
-  (simplify
-    (convert (op:s (convert@2 @0) (convert?@3 @1)))
-    (if (INTEGRAL_TYPE_P (type)
-	 /* We check for type compatibility between @0 and @1 below,
-	    so there's no need to check that @1/@3 are integral types.  */
-	 && INTEGRAL_TYPE_P (TREE_TYPE (@0))
-	 && INTEGRAL_TYPE_P (TREE_TYPE (@2))
-	 /* The precision of the type of each operand must match the
-	    precision of the mode of each operand, similarly for the
-	    result.  */
-	 && type_has_mode_precision_p (TREE_TYPE (@0))
-	 && type_has_mode_precision_p (TREE_TYPE (@1))
-	 && type_has_mode_precision_p (type)
-	 /* The inner conversion must be a widening conversion.  */
-	 && TYPE_PRECISION (TREE_TYPE (@2)) > TYPE_PRECISION (TREE_TYPE (@0))
-	 && types_match (@0, type)
-	 && (types_match (@0, @1)
-	     /* Or the second operand is const integer or converted const
-		integer from valueize.  */
-	     || TREE_CODE (@1) == INTEGER_CST))
-      (if (TYPE_OVERFLOW_WRAPS (TREE_TYPE (@0)))
-	(op @0 (convert @1))
-	(with { tree utype = unsigned_type_for (TREE_TYPE (@0)); }
-	 (convert (op (convert:utype @0)
-		      (convert:utype @1))))))))
+/* Convert (outertype)((innertype0)a+(innertype1)b)
+   into ((newtype)a+(newtype)b) where newtype
+   is the widest mode from all of these.  */
+(for op (plus minus mult rdiv)
+ (simplify
+   (convert (op:s@0 (convert1?@3 @1) (convert2?@4 @2)))
+   (with { tree arg0 = strip_float_extensions (@1);
+	   tree arg1 = strip_float_extensions (@2);
+	   tree itype = TREE_TYPE (@0);
+	   tree ty1 = TREE_TYPE (arg0);
+	   tree ty2 = TREE_TYPE (arg1);
+	   enum tree_code code = TREE_CODE (itype); }
+    (switch
+      (if (FLOAT_TYPE_P (ty1)
+	   && FLOAT_TYPE_P (ty2)
+	   && FLOAT_TYPE_P (type)
+	   && DECIMAL_FLOAT_TYPE_P (itype) == DECIMAL_FLOAT_TYPE_P (type))
+	 (with { tree newtype = type;
+		 if (TYPE_MODE (ty1) == SDmode
+		     || TYPE_MODE (ty2) == SDmode
+		     || TYPE_MODE (type) == SDmode)
+		   newtype = dfloat32_type_node;
+		 if (TYPE_MODE (ty1) == DDmode
+		     || TYPE_MODE (ty2) == DDmode
+		     || TYPE_MODE (type) == DDmode)
+		   newtype = dfloat64_type_node;
+		 if (TYPE_MODE (ty1) == TDmode
+		     || TYPE_MODE (ty2) == TDmode
+		     || TYPE_MODE (type) == TDmode)
+		   newtype = dfloat128_type_node; }
+	  (if ((newtype == dfloat32_type_node
+		|| newtype == dfloat64_type_node
+		|| newtype == dfloat128_type_node)
+	       && newtype == type)
+	     (convert:newtype (op (convert:newtype @1) (convert:newtype @2)))
+	     (with { if (TYPE_PRECISION (ty1) > TYPE_PRECISION (newtype))
+		       newtype = ty1;
+		     if (TYPE_PRECISION (ty2) > TYPE_PRECISION (newtype))
+		       newtype = ty2; }
+		/* Sometimes this transformation is safe (cannot
+		   change results through affecting double rounding
+		   cases) and sometimes it is not.  If NEWTYPE is
+		   wider than TYPE, e.g. (float)((long double)double
+		   + (long double)double) converted to
+		   (float)(double + double), the transformation is
+		   unsafe regardless of the details of the types
+		   involved; double rounding can arise if the result
+		   of NEWTYPE arithmetic is a NEWTYPE value half way
+		   between two representable TYPE values but the
+		   exact value is sufficiently different (in the
+		   right direction) for this difference to be
+		   visible in ITYPE arithmetic.  If NEWTYPE is the
+		   same as TYPE, however, the transformation may be
+		   safe depending on the types involved: it is safe
+		   if the ITYPE has strictly more than twice as many
+		   mantissa bits as TYPE, can represent infinities
+		   and NaNs if the TYPE can, and has sufficient
+		   exponent range for the product or ratio of two
+		   values representable in the TYPE to be within the
+		   range of normal values of ITYPE.  */
+	       (if (TYPE_PRECISION (newtype) < TYPE_PRECISION (itype)
+		    && (flag_unsafe_math_optimizations
+		        || (TYPE_PRECISION (newtype) == TYPE_PRECISION (type)
+			    && real_can_shorten_arithmetic (TYPE_MODE (itype),
+							    TYPE_MODE (type))
+			    && !excess_precision_type (newtype))))
+		  (convert:newtype (op (convert:newtype @1) (convert:newtype @2)))
+	  )))) )
+
+      (if (code == REAL_TYPE)
+	/* Ignore the conversion if we don't need to store intermediate
+	   results and neither type is a decimal float.  */
+	  (if (!(flag_float_store
+	       || DECIMAL_FLOAT_TYPE_P (type)
+	       || DECIMAL_FLOAT_TYPE_P (itype))
+	      && types_match (ty1, ty2))
+	    (convert (op (convert:ty1 @1) (convert:ty2 @2)))))
+
+      /* If we have a narrowing conversion of an arithmetic operation where
+	 both operands are widening conversions from the same type as the outer
+	 narrowing conversion.  Then convert the innermost operands to a
+	 suitable unsigned type (to avoid introducing undefined behavior),
+	 perform the operation and convert the result to the desired type.  */
+      (if (INTEGRAL_TYPE_P (type)
+	   && op != MULT_EXPR
+	   && op != RDIV_EXPR
+	   /* We check for type compatibility between @0 and @1 below,
+	      so there's no need to check that @2/@4 are integral types.  */
+	   && INTEGRAL_TYPE_P (TREE_TYPE (@1))
+	   && INTEGRAL_TYPE_P (TREE_TYPE (@3))
+	   /* The precision of the type of each operand must match the
+	      precision of the mode of each operand, similarly for the
+	      result.  */
+	   && type_has_mode_precision_p (TREE_TYPE (@1))
+	   && type_has_mode_precision_p (TREE_TYPE (@2))
+	   && type_has_mode_precision_p (type)
+	   /* The inner conversion must be a widening conversion.  */
+	   && TYPE_PRECISION (TREE_TYPE (@3)) > TYPE_PRECISION (TREE_TYPE (@1))
+	   && types_match (@1, type)
+	   && (types_match (@1, @2)
+	       /* Or the second operand is const integer or converted const
+		  integer from valueize.  */
+	       || TREE_CODE (@2) == INTEGER_CST))
+        (if (TYPE_OVERFLOW_WRAPS (TREE_TYPE (@1)))
+	  (op @1 (convert @2))
+	  (with { tree utype = unsigned_type_for (TREE_TYPE (@1)); }
+	   (convert (op (convert:utype @1)
+		        (convert:utype @2))))))
+    )
+)))
 
 /* This is another case of narrowing, specifically when there's an outer
    BIT_AND_EXPR which masks off bits outside the type of the innermost
diff --git a/gcc/testsuite/gcc.dg/type-convert-var.c b/gcc/testsuite/gcc.dg/type-convert-var.c
new file mode 100644
index 0000000000000000000000000000000000000000..88d74e2a49d7123515b87ff64a18bd9b306d57e9
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/type-convert-var.c
@@ -0,0 +1,9 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-O1 -fdump-tree-optimized" } */
+void foo (float a, float b, float *c)
+{
+  double e = (double)a * (double)b;
+  *c = (float)e;
+}
+
+/* { dg-final { scan-tree-dump-not {double} "optimized" } } */

Reply via email to