Commit 543a9bcd by Richard Sandiford Committed by Richard Sandiford

Move int rounding folds to match.pd

Tested on x86_64-linux-gnu, aarch64-linux-gnu and arm-linux-gnueabi.

gcc/
	* builtins.c (do_real_to_int_conversion): New function.
	(fold_fixed_mathfn, fold_builtin_int_roundingfn): Delete.
	(fold_builtin_1): Handle constant {i,l,ll}{ceil,floor,round}{f,,l}
	arguments here.
	* match.pd: Add rules previously handled by fold_fixed_mathfn
	and fold_builtin_int_roundingfn.

gcc/testsuite/
	* gcc.dg/torture/builtin-minmax-1.c: Don't run at -O0.

From-SVN: r229311
parent 257aecb4
2015-10-26 Richard Sandiford <richard.sandiford@arm.com>
* builtins.c (do_real_to_int_conversion): New function.
(fold_fixed_mathfn, fold_builtin_int_roundingfn): Delete.
(fold_builtin_1): Handle constant {i,l,ll}{ceil,floor,round}{f,,l}
arguments here.
* match.pd: Add rules previously handled by fold_fixed_mathfn
and fold_builtin_int_roundingfn.
2015-10-26 Richard Sandiford <richard.sandiford@arm.com>
* match.pd: Use macros to define built-in operator lists.
2015-10-20 Richard Sandiford <richard.sandiford@arm.com>
......@@ -158,7 +158,6 @@ static rtx expand_builtin_fabs (tree, rtx, rtx);
static rtx expand_builtin_signbit (tree, rtx);
static tree fold_builtin_pow (location_t, tree, tree, tree, tree);
static tree fold_builtin_powi (location_t, tree, tree, tree, tree);
static tree fold_builtin_int_roundingfn (location_t, tree, tree);
static tree fold_builtin_bitop (tree, tree);
static tree fold_builtin_strchr (location_t, tree, tree, tree);
static tree fold_builtin_memchr (location_t, tree, tree, tree, tree);
......@@ -7273,6 +7272,35 @@ fold_builtin_strlen (location_t loc, tree type, tree arg)
}
}
/* If ARG is a foldable constant real, use FN to round it to an integer
value and try to represent the result in integer type ITYPE. Return
the value on success, otherwise return null. */
static tree
do_real_to_int_conversion (tree itype, tree arg,
void (*fn) (REAL_VALUE_TYPE *, machine_mode,
const REAL_VALUE_TYPE *))
{
if (TREE_CODE (arg) != REAL_CST || TREE_OVERFLOW (arg))
return NULL_TREE;
const REAL_VALUE_TYPE *value = TREE_REAL_CST_PTR (arg);
if (!real_isfinite (value))
return NULL_TREE;
tree ftype = TREE_TYPE (arg);
REAL_VALUE_TYPE rounded;
fn (&rounded, TYPE_MODE (ftype), value);
bool fail = false;
wide_int ival = real_to_integer (&rounded, &fail, TYPE_PRECISION (itype));
if (fail)
return NULL_TREE;
return wide_int_to_tree (itype, ival);
}
/* Fold a call to __builtin_inf or __builtin_huge_val. */
static tree
......@@ -7314,112 +7342,6 @@ fold_builtin_nan (tree arg, tree type, int quiet)
return build_real (type, real);
}
/* FNDECL is assumed to be builtin which can narrow the FP type of
the argument, for instance lround((double)f) -> lroundf (f).
Do the transformation for a call with argument ARG. */
static tree
fold_fixed_mathfn (location_t loc, tree fndecl, tree arg)
{
enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
if (!validate_arg (arg, REAL_TYPE))
return NULL_TREE;
/* If argument is already integer valued, and we don't need to worry
about setting errno, there's no need to perform rounding. */
if (! flag_errno_math && integer_valued_real_p (arg))
return fold_build1_loc (loc, FIX_TRUNC_EXPR,
TREE_TYPE (TREE_TYPE (fndecl)), arg);
if (optimize)
{
tree ftype = TREE_TYPE (arg);
tree arg0 = strip_float_extensions (arg);
tree newtype = TREE_TYPE (arg0);
tree decl;
if (TYPE_PRECISION (newtype) < TYPE_PRECISION (ftype)
&& (decl = mathfn_built_in (newtype, fcode)))
return build_call_expr_loc (loc, decl, 1,
fold_convert_loc (loc, newtype, arg0));
}
/* Canonicalize iround (x) to lround (x) on ILP32 targets where
sizeof (int) == sizeof (long). */
if (TYPE_PRECISION (integer_type_node)
== TYPE_PRECISION (long_integer_type_node))
{
tree newfn = NULL_TREE;
switch (fcode)
{
CASE_FLT_FN (BUILT_IN_ICEIL):
newfn = mathfn_built_in (TREE_TYPE (arg), BUILT_IN_LCEIL);
break;
CASE_FLT_FN (BUILT_IN_IFLOOR):
newfn = mathfn_built_in (TREE_TYPE (arg), BUILT_IN_LFLOOR);
break;
CASE_FLT_FN (BUILT_IN_IROUND):
newfn = mathfn_built_in (TREE_TYPE (arg), BUILT_IN_LROUND);
break;
CASE_FLT_FN (BUILT_IN_IRINT):
newfn = mathfn_built_in (TREE_TYPE (arg), BUILT_IN_LRINT);
break;
default:
break;
}
if (newfn)
{
tree newcall = build_call_expr_loc (loc, newfn, 1, arg);
return fold_convert_loc (loc,
TREE_TYPE (TREE_TYPE (fndecl)), newcall);
}
}
/* Canonicalize llround (x) to lround (x) on LP64 targets where
sizeof (long long) == sizeof (long). */
if (TYPE_PRECISION (long_long_integer_type_node)
== TYPE_PRECISION (long_integer_type_node))
{
tree newfn = NULL_TREE;
switch (fcode)
{
CASE_FLT_FN (BUILT_IN_LLCEIL):
newfn = mathfn_built_in (TREE_TYPE (arg), BUILT_IN_LCEIL);
break;
CASE_FLT_FN (BUILT_IN_LLFLOOR):
newfn = mathfn_built_in (TREE_TYPE (arg), BUILT_IN_LFLOOR);
break;
CASE_FLT_FN (BUILT_IN_LLROUND):
newfn = mathfn_built_in (TREE_TYPE (arg), BUILT_IN_LROUND);
break;
CASE_FLT_FN (BUILT_IN_LLRINT):
newfn = mathfn_built_in (TREE_TYPE (arg), BUILT_IN_LRINT);
break;
default:
break;
}
if (newfn)
{
tree newcall = build_call_expr_loc (loc, newfn, 1, arg);
return fold_convert_loc (loc,
TREE_TYPE (TREE_TYPE (fndecl)), newcall);
}
}
return NULL_TREE;
}
/* Fold function call to builtin sincos, sincosf, or sincosl. Return
NULL_TREE if no simplification can be made. */
......@@ -7460,74 +7382,6 @@ fold_builtin_sincos (location_t loc,
build1 (REALPART_EXPR, type, call)));
}
/* Fold function call to builtin lround, lroundf or lroundl (or the
corresponding long long versions) and other rounding functions. ARG
is the argument to the call. Return NULL_TREE if no simplification
can be made. */
static tree
fold_builtin_int_roundingfn (location_t loc, tree fndecl, tree arg)
{
if (!validate_arg (arg, REAL_TYPE))
return NULL_TREE;
/* Optimize lround of constant value. */
if (TREE_CODE (arg) == REAL_CST && !TREE_OVERFLOW (arg))
{
const REAL_VALUE_TYPE x = TREE_REAL_CST (arg);
if (real_isfinite (&x))
{
tree itype = TREE_TYPE (TREE_TYPE (fndecl));
tree ftype = TREE_TYPE (arg);
REAL_VALUE_TYPE r;
bool fail = false;
switch (DECL_FUNCTION_CODE (fndecl))
{
CASE_FLT_FN (BUILT_IN_IFLOOR):
CASE_FLT_FN (BUILT_IN_LFLOOR):
CASE_FLT_FN (BUILT_IN_LLFLOOR):
real_floor (&r, TYPE_MODE (ftype), &x);
break;
CASE_FLT_FN (BUILT_IN_ICEIL):
CASE_FLT_FN (BUILT_IN_LCEIL):
CASE_FLT_FN (BUILT_IN_LLCEIL):
real_ceil (&r, TYPE_MODE (ftype), &x);
break;
CASE_FLT_FN (BUILT_IN_IROUND):
CASE_FLT_FN (BUILT_IN_LROUND):
CASE_FLT_FN (BUILT_IN_LLROUND):
real_round (&r, TYPE_MODE (ftype), &x);
break;
default:
gcc_unreachable ();
}
wide_int val = real_to_integer (&r, &fail, TYPE_PRECISION (itype));
if (!fail)
return wide_int_to_tree (itype, val);
}
}
switch (DECL_FUNCTION_CODE (fndecl))
{
CASE_FLT_FN (BUILT_IN_LFLOOR):
CASE_FLT_FN (BUILT_IN_LLFLOOR):
/* Fold lfloor (x) where x is nonnegative to FIX_TRUNC (x). */
if (tree_expr_nonnegative_p (arg))
return fold_build1_loc (loc, FIX_TRUNC_EXPR,
TREE_TYPE (TREE_TYPE (fndecl)), arg);
break;
default:;
}
return fold_fixed_mathfn (loc, fndecl, arg);
}
/* Fold function call to builtin ffs, clz, ctz, popcount and parity
and their long and long long variants (i.e. ffsl and ffsll). ARG is
the argument to the call. Return NULL_TREE if no simplification can
......@@ -9453,18 +9307,23 @@ fold_builtin_1 (location_t loc, tree fndecl, tree arg0)
CASE_FLT_FN (BUILT_IN_ICEIL):
CASE_FLT_FN (BUILT_IN_LCEIL):
CASE_FLT_FN (BUILT_IN_LLCEIL):
return do_real_to_int_conversion (type, arg0, real_ceil);
CASE_FLT_FN (BUILT_IN_LFLOOR):
CASE_FLT_FN (BUILT_IN_IFLOOR):
CASE_FLT_FN (BUILT_IN_LLFLOOR):
return do_real_to_int_conversion (type, arg0, real_floor);
CASE_FLT_FN (BUILT_IN_IROUND):
CASE_FLT_FN (BUILT_IN_LROUND):
CASE_FLT_FN (BUILT_IN_LLROUND):
return fold_builtin_int_roundingfn (loc, fndecl, arg0);
return do_real_to_int_conversion (type, arg0, real_round);
CASE_FLT_FN (BUILT_IN_IRINT):
CASE_FLT_FN (BUILT_IN_LRINT):
CASE_FLT_FN (BUILT_IN_LLRINT):
return fold_fixed_mathfn (loc, fndecl, arg0);
/* Not yet folded to a constant. */
return NULL_TREE;
case BUILT_IN_BSWAP16:
case BUILT_IN_BSWAP32:
......
......@@ -52,6 +52,30 @@ along with GCC; see the file COPYING3. If not see
#define DEFINE_MATH_FN(FN) \
(define_operator_list FN BUILT_IN_##FN##F BUILT_IN_##FN BUILT_IN_##FN##L)
/* Define operand lists for math rounding functions {,i,l,ll}FN,
where the versions prefixed with "i" return an int, those prefixed with
"l" return a long and those prefixed with "ll" return a long long.
Also define operand lists:
X<FN>F for all float functions, in the order i, l, ll
X<FN> for all double functions, in the same order
X<FN>L for all long double functions, in the same order. */
#define DEFINE_INT_AND_FLOAT_ROUND_FN(FN) \
DEFINE_MATH_FN (FN) \
DEFINE_MATH_FN (I##FN) \
DEFINE_MATH_FN (L##FN) \
DEFINE_MATH_FN (LL##FN) \
(define_operator_list X##FN##F BUILT_IN_I##FN##F \
BUILT_IN_L##FN##F \
BUILT_IN_LL##FN##F) \
(define_operator_list X##FN BUILT_IN_I##FN \
BUILT_IN_L##FN \
BUILT_IN_LL##FN) \
(define_operator_list X##FN##L BUILT_IN_I##FN##L \
BUILT_IN_L##FN##L \
BUILT_IN_LL##FN##L)
DEFINE_MATH_FN (LOG)
DEFINE_MATH_FN (EXP)
DEFINE_MATH_FN (LOG2)
......@@ -76,11 +100,12 @@ DEFINE_MATH_FN (HYPOT)
DEFINE_MATH_FN (COPYSIGN)
DEFINE_MATH_FN (CABS)
DEFINE_MATH_FN (TRUNC)
DEFINE_MATH_FN (FLOOR)
DEFINE_MATH_FN (CEIL)
DEFINE_MATH_FN (ROUND)
DEFINE_MATH_FN (NEARBYINT)
DEFINE_MATH_FN (RINT)
DEFINE_INT_AND_FLOAT_ROUND_FN (FLOOR)
DEFINE_INT_AND_FLOAT_ROUND_FN (CEIL)
DEFINE_INT_AND_FLOAT_ROUND_FN (ROUND)
DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
/* Simplifications of operations with one constant operand and
simplifications to constants or single values. */
......@@ -2655,6 +2680,65 @@ DEFINE_MATH_FN (RINT)
(froms (convert float_value_p@0))
(convert (tos @0)))))
(for froms (XFLOORL XCEILL XROUNDL XRINTL)
tos (XFLOOR XCEIL XROUND XRINT)
/* llfloorl(extend(x)) -> llfloor(x), etc., if x is a double. */
(if (optimize && canonicalize_math_p ())
(simplify
(froms (convert double_value_p@0))
(tos @0))))
(for froms (XFLOORL XCEILL XROUNDL XRINTL
XFLOOR XCEIL XROUND XRINT)
tos (XFLOORF XCEILF XROUNDF XRINTF)
/* llfloorl(extend(x)) and llfloor(extend(x)) -> llfloorf(x), etc.,
if x is a float. */
(if (optimize && canonicalize_math_p ())
(simplify
(froms (convert float_value_p@0))
(tos @0))))
(if (canonicalize_math_p ())
/* xfloor(x) -> fix_trunc(x) if x is nonnegative. */
(for floors (IFLOOR LFLOOR LLFLOOR)
(simplify
(floors tree_expr_nonnegative_p@0)
(fix_trunc @0))))
(if (canonicalize_math_p ())
/* xfloor(x) -> fix_trunc(x), etc., if x is integer valued. */
(for fns (IFLOOR LFLOOR LLFLOOR
ICEIL LCEIL LLCEIL
IROUND LROUND LLROUND)
(simplify
(fns integer_valued_real_p@0)
(fix_trunc @0)))
(if (!flag_errno_math)
/* xrint(x) -> fix_trunc(x), etc., if x is integer valued. */
(for rints (IRINT LRINT LLRINT)
(simplify
(rints integer_valued_real_p@0)
(fix_trunc @0)))))
(if (canonicalize_math_p ())
(for ifn (IFLOOR ICEIL IROUND IRINT)
lfn (LFLOOR LCEIL LROUND LRINT)
llfn (LLFLOOR LLCEIL LLROUND LLRINT)
/* Canonicalize iround (x) to lround (x) on ILP32 targets where
sizeof (int) == sizeof (long). */
(if (TYPE_PRECISION (integer_type_node)
== TYPE_PRECISION (long_integer_type_node))
(simplify
(ifn @0)
(lfn:long_integer_type_node @0)))
/* Canonicalize llround (x) to lround (x) on LP64 targets where
sizeof (long long) == sizeof (long). */
(if (TYPE_PRECISION (long_long_integer_type_node)
== TYPE_PRECISION (long_integer_type_node))
(simplify
(llfn @0)
(lfn:long_integer_type_node @0)))))
/* cproj(x) -> x if we're ignoring infinities. */
(simplify
(CPROJ @0)
......
2015-10-26 Richard Sandiford <richard.sandiford@arm.com>
* gcc.dg/torture/builtin-minmax-1.c: Don't run at -O0.
2015-01-25 Paul Thomas <pault@gcc.gnu.org>
PR fortran/67171
......
......@@ -7,6 +7,7 @@
/* { dg-do link } */
/* { dg-options "-fno-math-errno" } */
/* { dg-skip-if "" { *-*-* } { "-O0" } { "" } } */
/* All references to link_error should go away at compile-time. */
extern void link_error(int);
......
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