Commit 8f5331b2 by Tamar Christina Committed by Tamar Christina

Add rules to strip away unneeded type casts in expressions

This patch moves part of the type conversion code from convert.c to match.pd
because match.pd is able to apply these transformations in the presence of
intermediate temporary variables.

Concretely it makes both these cases behave the same

  float e = (float)a * (float)b;
  *c = (_Float16)e;

and 

  *c = (_Float16)((float)a * (float)b);

gcc/ChangeLog:

	* convert.c (convert_to_real_1): Move part of conversion code...
	* match.pd: ...To here.

gcc/testsuite/ChangeLog:

	* gcc.dg/type-convert-var.c: New test.

From-SVN: r273826
parent 4517b378
2019-07-26 Tamar Christina <tamar.christina@arm.com>
* convert.c (convert_to_real_1): Move part of conversion code...
* match.pd: ...To here.
2019-07-26 Martin Jambor <mjambor@suse.cz>
PR ipa/89330
......
......@@ -298,92 +298,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;
}
......
......@@ -4938,37 +4938,109 @@ 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)))
/* 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)))))
(if (FLOAT_TYPE_P (type)
&& DECIMAL_FLOAT_TYPE_P (TREE_TYPE (@0))
== DECIMAL_FLOAT_TYPE_P (type))
(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); }
(if (FLOAT_TYPE_P (ty1)
&& FLOAT_TYPE_P (ty2))
(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
&& types_match (newtype, type))
(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)))
&& !types_match (itype, newtype))
(convert:type (op (convert:newtype @1)
(convert:newtype @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
......
2018-07-24 Tamar Christina <tamar.christina@arm.com>
* gcc.dg/type-convert-var.c: New test.
2019-07-26 Martin Jambor <mjambor@suse.cz>
PR ipa/89330
......
/* { 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" } } */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment