8sa1-gcc/gcc/fold-const-call.c
Eric Botcazou fe7ebef7fe Add support for __builtin_bswap128
This patch introduces a new builtin named __builtin_bswap128 on targets
where TImode is supported, i.e. 64-bit targets only in practice.  The
implementation simply reuses the existing double word path in optab, so
no routine is added to libgcc (which means that you get two calls to
_bswapdi2 in the worst case).

gcc/ChangeLog:

	* builtin-types.def (BT_UINT128): New primitive type.
	(BT_FN_UINT128_UINT128): New function type.
	* builtins.def (BUILT_IN_BSWAP128): New GCC builtin.
	* doc/extend.texi (__builtin_bswap128): Document it.
	* builtins.c (expand_builtin): Deal with BUILT_IN_BSWAP128.
	(is_inexpensive_builtin): Likewise.
	* fold-const-call.c (fold_const_call_ss): Likewise.
	* fold-const.c (tree_call_nonnegative_warnv_p): Likewise.
	* tree-ssa-ccp.c (evaluate_stmt): Likewise.
	* tree-vect-stmts.c (vect_get_data_ptr_increment): Likewise.
	(vectorizable_call): Likewise.
	* optabs.c (expand_unop): Always use the double word path for it.
	* tree-core.h (enum tree_index): Add TI_UINT128_TYPE.
	* tree.h (uint128_type_node): New global type.
	* tree.c (build_common_tree_nodes): Build it if TImode is supported.

gcc/testsuite/ChangeLog:

	* gcc.dg/builtin-bswap-10.c: New test.
	* gcc.dg/builtin-bswap-11.c: Likewise.
	* gcc.dg/builtin-bswap-12.c: Likewise.
	* gcc.target/i386/builtin-bswap-5.c: Likewise.
2020-05-28 00:33:04 +02:00

1841 lines
49 KiB
C

/* Constant folding for calls to built-in and internal functions.
Copyright (C) 1988-2020 Free Software Foundation, Inc.
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation; either version 3, or (at your option) any later
version.
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "realmpfr.h"
#include "tree.h"
#include "stor-layout.h"
#include "options.h"
#include "fold-const.h"
#include "fold-const-call.h"
#include "case-cfn-macros.h"
#include "tm.h" /* For C[LT]Z_DEFINED_AT_ZERO. */
#include "builtins.h"
#include "gimple-expr.h"
#include "tree-vector-builder.h"
/* Functions that test for certain constant types, abstracting away the
decision about whether to check for overflow. */
static inline bool
integer_cst_p (tree t)
{
return TREE_CODE (t) == INTEGER_CST && !TREE_OVERFLOW (t);
}
static inline bool
real_cst_p (tree t)
{
return TREE_CODE (t) == REAL_CST && !TREE_OVERFLOW (t);
}
static inline bool
complex_cst_p (tree t)
{
return TREE_CODE (t) == COMPLEX_CST;
}
/* Return true if ARG is a constant in the range of the host size_t.
Store it in *SIZE_OUT if so. */
static inline bool
host_size_t_cst_p (tree t, size_t *size_out)
{
if (types_compatible_p (size_type_node, TREE_TYPE (t))
&& integer_cst_p (t)
&& (wi::min_precision (wi::to_wide (t), UNSIGNED)
<= sizeof (size_t) * CHAR_BIT))
{
*size_out = tree_to_uhwi (t);
return true;
}
return false;
}
/* RES is the result of a comparison in which < 0 means "less", 0 means
"equal" and > 0 means "more". Canonicalize it to -1, 0 or 1 and
return it in type TYPE. */
tree
build_cmp_result (tree type, int res)
{
return build_int_cst (type, res < 0 ? -1 : res > 0 ? 1 : 0);
}
/* M is the result of trying to constant-fold an expression (starting
with clear MPFR flags) and INEXACT says whether the result in M is
exact or inexact. Return true if M can be used as a constant-folded
result in format FORMAT, storing the value in *RESULT if so. */
static bool
do_mpfr_ckconv (real_value *result, mpfr_srcptr m, bool inexact,
const real_format *format)
{
/* Proceed iff we get a normal number, i.e. not NaN or Inf and no
overflow/underflow occurred. If -frounding-math, proceed iff the
result of calling FUNC was exact. */
if (!mpfr_number_p (m)
|| mpfr_overflow_p ()
|| mpfr_underflow_p ()
|| (flag_rounding_math && inexact))
return false;
REAL_VALUE_TYPE tmp;
real_from_mpfr (&tmp, m, format, MPFR_RNDN);
/* Proceed iff GCC's REAL_VALUE_TYPE can hold the MPFR values.
If the REAL_VALUE_TYPE is zero but the mpft_t is not, then we
underflowed in the conversion. */
if (!real_isfinite (&tmp)
|| ((tmp.cl == rvc_zero) != (mpfr_zero_p (m) != 0)))
return false;
real_convert (result, format, &tmp);
return real_identical (result, &tmp);
}
/* Try to evaluate:
*RESULT = f (*ARG)
in format FORMAT, given that FUNC is the MPFR implementation of f.
Return true on success. */
static bool
do_mpfr_arg1 (real_value *result,
int (*func) (mpfr_ptr, mpfr_srcptr, mpfr_rnd_t),
const real_value *arg, const real_format *format)
{
/* To proceed, MPFR must exactly represent the target floating point
format, which only happens when the target base equals two. */
if (format->b != 2 || !real_isfinite (arg))
return false;
int prec = format->p;
mpfr_rnd_t rnd = format->round_towards_zero ? MPFR_RNDZ : MPFR_RNDN;
mpfr_t m;
mpfr_init2 (m, prec);
mpfr_from_real (m, arg, MPFR_RNDN);
mpfr_clear_flags ();
bool inexact = func (m, m, rnd);
bool ok = do_mpfr_ckconv (result, m, inexact, format);
mpfr_clear (m);
return ok;
}
/* Try to evaluate:
*RESULT_SIN = sin (*ARG);
*RESULT_COS = cos (*ARG);
for format FORMAT. Return true on success. */
static bool
do_mpfr_sincos (real_value *result_sin, real_value *result_cos,
const real_value *arg, const real_format *format)
{
/* To proceed, MPFR must exactly represent the target floating point
format, which only happens when the target base equals two. */
if (format->b != 2 || !real_isfinite (arg))
return false;
int prec = format->p;
mpfr_rnd_t rnd = format->round_towards_zero ? MPFR_RNDZ : MPFR_RNDN;
mpfr_t m, ms, mc;
mpfr_inits2 (prec, m, ms, mc, NULL);
mpfr_from_real (m, arg, MPFR_RNDN);
mpfr_clear_flags ();
bool inexact = mpfr_sin_cos (ms, mc, m, rnd);
bool ok = (do_mpfr_ckconv (result_sin, ms, inexact, format)
&& do_mpfr_ckconv (result_cos, mc, inexact, format));
mpfr_clears (m, ms, mc, NULL);
return ok;
}
/* Try to evaluate:
*RESULT = f (*ARG0, *ARG1)
in format FORMAT, given that FUNC is the MPFR implementation of f.
Return true on success. */
static bool
do_mpfr_arg2 (real_value *result,
int (*func) (mpfr_ptr, mpfr_srcptr, mpfr_srcptr, mpfr_rnd_t),
const real_value *arg0, const real_value *arg1,
const real_format *format)
{
/* To proceed, MPFR must exactly represent the target floating point
format, which only happens when the target base equals two. */
if (format->b != 2 || !real_isfinite (arg0) || !real_isfinite (arg1))
return false;
int prec = format->p;
mpfr_rnd_t rnd = format->round_towards_zero ? MPFR_RNDZ : MPFR_RNDN;
mpfr_t m0, m1;
mpfr_inits2 (prec, m0, m1, NULL);
mpfr_from_real (m0, arg0, MPFR_RNDN);
mpfr_from_real (m1, arg1, MPFR_RNDN);
mpfr_clear_flags ();
bool inexact = func (m0, m0, m1, rnd);
bool ok = do_mpfr_ckconv (result, m0, inexact, format);
mpfr_clears (m0, m1, NULL);
return ok;
}
/* Try to evaluate:
*RESULT = f (ARG0, *ARG1)
in format FORMAT, given that FUNC is the MPFR implementation of f.
Return true on success. */
static bool
do_mpfr_arg2 (real_value *result,
int (*func) (mpfr_ptr, long, mpfr_srcptr, mpfr_rnd_t),
const wide_int_ref &arg0, const real_value *arg1,
const real_format *format)
{
if (format->b != 2 || !real_isfinite (arg1))
return false;
int prec = format->p;
mpfr_rnd_t rnd = format->round_towards_zero ? MPFR_RNDZ : MPFR_RNDN;
mpfr_t m;
mpfr_init2 (m, prec);
mpfr_from_real (m, arg1, MPFR_RNDN);
mpfr_clear_flags ();
bool inexact = func (m, arg0.to_shwi (), m, rnd);
bool ok = do_mpfr_ckconv (result, m, inexact, format);
mpfr_clear (m);
return ok;
}
/* Try to evaluate:
*RESULT = f (*ARG0, *ARG1, *ARG2)
in format FORMAT, given that FUNC is the MPFR implementation of f.
Return true on success. */
static bool
do_mpfr_arg3 (real_value *result,
int (*func) (mpfr_ptr, mpfr_srcptr, mpfr_srcptr,
mpfr_srcptr, mpfr_rnd_t),
const real_value *arg0, const real_value *arg1,
const real_value *arg2, const real_format *format)
{
/* To proceed, MPFR must exactly represent the target floating point
format, which only happens when the target base equals two. */
if (format->b != 2
|| !real_isfinite (arg0)
|| !real_isfinite (arg1)
|| !real_isfinite (arg2))
return false;
int prec = format->p;
mpfr_rnd_t rnd = format->round_towards_zero ? MPFR_RNDZ : MPFR_RNDN;
mpfr_t m0, m1, m2;
mpfr_inits2 (prec, m0, m1, m2, NULL);
mpfr_from_real (m0, arg0, MPFR_RNDN);
mpfr_from_real (m1, arg1, MPFR_RNDN);
mpfr_from_real (m2, arg2, MPFR_RNDN);
mpfr_clear_flags ();
bool inexact = func (m0, m0, m1, m2, rnd);
bool ok = do_mpfr_ckconv (result, m0, inexact, format);
mpfr_clears (m0, m1, m2, NULL);
return ok;
}
/* M is the result of trying to constant-fold an expression (starting
with clear MPFR flags) and INEXACT says whether the result in M is
exact or inexact. Return true if M can be used as a constant-folded
result in which the real and imaginary parts have format FORMAT.
Store those parts in *RESULT_REAL and *RESULT_IMAG if so. */
static bool
do_mpc_ckconv (real_value *result_real, real_value *result_imag,
mpc_srcptr m, bool inexact, const real_format *format)
{
/* Proceed iff we get a normal number, i.e. not NaN or Inf and no
overflow/underflow occurred. If -frounding-math, proceed iff the
result of calling FUNC was exact. */
if (!mpfr_number_p (mpc_realref (m))
|| !mpfr_number_p (mpc_imagref (m))
|| mpfr_overflow_p ()
|| mpfr_underflow_p ()
|| (flag_rounding_math && inexact))
return false;
REAL_VALUE_TYPE tmp_real, tmp_imag;
real_from_mpfr (&tmp_real, mpc_realref (m), format, MPFR_RNDN);
real_from_mpfr (&tmp_imag, mpc_imagref (m), format, MPFR_RNDN);
/* Proceed iff GCC's REAL_VALUE_TYPE can hold the MPFR values.
If the REAL_VALUE_TYPE is zero but the mpft_t is not, then we
underflowed in the conversion. */
if (!real_isfinite (&tmp_real)
|| !real_isfinite (&tmp_imag)
|| (tmp_real.cl == rvc_zero) != (mpfr_zero_p (mpc_realref (m)) != 0)
|| (tmp_imag.cl == rvc_zero) != (mpfr_zero_p (mpc_imagref (m)) != 0))
return false;
real_convert (result_real, format, &tmp_real);
real_convert (result_imag, format, &tmp_imag);
return (real_identical (result_real, &tmp_real)
&& real_identical (result_imag, &tmp_imag));
}
/* Try to evaluate:
RESULT = f (ARG)
in format FORMAT, given that FUNC is the mpc implementation of f.
Return true on success. Both RESULT and ARG are represented as
real and imaginary pairs. */
static bool
do_mpc_arg1 (real_value *result_real, real_value *result_imag,
int (*func) (mpc_ptr, mpc_srcptr, mpc_rnd_t),
const real_value *arg_real, const real_value *arg_imag,
const real_format *format)
{
/* To proceed, MPFR must exactly represent the target floating point
format, which only happens when the target base equals two. */
if (format->b != 2
|| !real_isfinite (arg_real)
|| !real_isfinite (arg_imag))
return false;
int prec = format->p;
mpc_rnd_t crnd = format->round_towards_zero ? MPC_RNDZZ : MPC_RNDNN;
mpc_t m;
mpc_init2 (m, prec);
mpfr_from_real (mpc_realref (m), arg_real, MPFR_RNDN);
mpfr_from_real (mpc_imagref (m), arg_imag, MPFR_RNDN);
mpfr_clear_flags ();
bool inexact = func (m, m, crnd);
bool ok = do_mpc_ckconv (result_real, result_imag, m, inexact, format);
mpc_clear (m);
return ok;
}
/* Try to evaluate:
RESULT = f (ARG0, ARG1)
in format FORMAT, given that FUNC is the mpc implementation of f.
Return true on success. RESULT, ARG0 and ARG1 are represented as
real and imaginary pairs. */
static bool
do_mpc_arg2 (real_value *result_real, real_value *result_imag,
int (*func)(mpc_ptr, mpc_srcptr, mpc_srcptr, mpc_rnd_t),
const real_value *arg0_real, const real_value *arg0_imag,
const real_value *arg1_real, const real_value *arg1_imag,
const real_format *format)
{
if (!real_isfinite (arg0_real)
|| !real_isfinite (arg0_imag)
|| !real_isfinite (arg1_real)
|| !real_isfinite (arg1_imag))
return false;
int prec = format->p;
mpc_rnd_t crnd = format->round_towards_zero ? MPC_RNDZZ : MPC_RNDNN;
mpc_t m0, m1;
mpc_init2 (m0, prec);
mpc_init2 (m1, prec);
mpfr_from_real (mpc_realref (m0), arg0_real, MPFR_RNDN);
mpfr_from_real (mpc_imagref (m0), arg0_imag, MPFR_RNDN);
mpfr_from_real (mpc_realref (m1), arg1_real, MPFR_RNDN);
mpfr_from_real (mpc_imagref (m1), arg1_imag, MPFR_RNDN);
mpfr_clear_flags ();
bool inexact = func (m0, m0, m1, crnd);
bool ok = do_mpc_ckconv (result_real, result_imag, m0, inexact, format);
mpc_clear (m0);
mpc_clear (m1);
return ok;
}
/* Try to evaluate:
*RESULT = logb (*ARG)
in format FORMAT. Return true on success. */
static bool
fold_const_logb (real_value *result, const real_value *arg,
const real_format *format)
{
switch (arg->cl)
{
case rvc_nan:
/* If arg is +-NaN, then return it. */
*result = *arg;
return true;
case rvc_inf:
/* If arg is +-Inf, then return +Inf. */
*result = *arg;
result->sign = 0;
return true;
case rvc_zero:
/* Zero may set errno and/or raise an exception. */
return false;
case rvc_normal:
/* For normal numbers, proceed iff radix == 2. In GCC,
normalized significands are in the range [0.5, 1.0). We
want the exponent as if they were [1.0, 2.0) so get the
exponent and subtract 1. */
if (format->b == 2)
{
real_from_integer (result, format, REAL_EXP (arg) - 1, SIGNED);
return true;
}
return false;
}
gcc_unreachable ();
}
/* Try to evaluate:
*RESULT = significand (*ARG)
in format FORMAT. Return true on success. */
static bool
fold_const_significand (real_value *result, const real_value *arg,
const real_format *format)
{
switch (arg->cl)
{
case rvc_zero:
case rvc_nan:
case rvc_inf:
/* If arg is +-0, +-Inf or +-NaN, then return it. */
*result = *arg;
return true;
case rvc_normal:
/* For normal numbers, proceed iff radix == 2. */
if (format->b == 2)
{
*result = *arg;
/* In GCC, normalized significands are in the range [0.5, 1.0).
We want them to be [1.0, 2.0) so set the exponent to 1. */
SET_REAL_EXP (result, 1);
return true;
}
return false;
}
gcc_unreachable ();
}
/* Try to evaluate:
*RESULT = f (*ARG)
where FORMAT is the format of *ARG and PRECISION is the number of
significant bits in the result. Return true on success. */
static bool
fold_const_conversion (wide_int *result,
void (*fn) (real_value *, format_helper,
const real_value *),
const real_value *arg, unsigned int precision,
const real_format *format)
{
if (!real_isfinite (arg))
return false;
real_value rounded;
fn (&rounded, format, arg);
bool fail = false;
*result = real_to_integer (&rounded, &fail, precision);
return !fail;
}
/* Try to evaluate:
*RESULT = pow (*ARG0, *ARG1)
in format FORMAT. Return true on success. */
static bool
fold_const_pow (real_value *result, const real_value *arg0,
const real_value *arg1, const real_format *format)
{
if (do_mpfr_arg2 (result, mpfr_pow, arg0, arg1, format))
return true;
/* Check for an integer exponent. */
REAL_VALUE_TYPE cint1;
HOST_WIDE_INT n1 = real_to_integer (arg1);
real_from_integer (&cint1, VOIDmode, n1, SIGNED);
/* Attempt to evaluate pow at compile-time, unless this should
raise an exception. */
if (real_identical (arg1, &cint1)
&& (n1 > 0
|| (!flag_trapping_math && !flag_errno_math)
|| !real_equal (arg0, &dconst0)))
{
bool inexact = real_powi (result, format, arg0, n1);
/* Avoid the folding if flag_signaling_nans is on. */
if (flag_unsafe_math_optimizations
|| (!inexact
&& !(flag_signaling_nans
&& REAL_VALUE_ISSIGNALING_NAN (*arg0))))
return true;
}
return false;
}
/* Try to evaluate:
*RESULT = nextafter (*ARG0, *ARG1)
or
*RESULT = nexttoward (*ARG0, *ARG1)
in format FORMAT. Return true on success. */
static bool
fold_const_nextafter (real_value *result, const real_value *arg0,
const real_value *arg1, const real_format *format)
{
if (REAL_VALUE_ISSIGNALING_NAN (*arg0)
|| REAL_VALUE_ISSIGNALING_NAN (*arg1))
return false;
/* Don't handle composite modes, nor decimal, nor modes without
inf or denorm at least for now. */
if (format->pnan < format->p
|| format->b == 10
|| !format->has_inf
|| !format->has_denorm)
return false;
if (real_nextafter (result, format, arg0, arg1)
/* If raising underflow or overflow and setting errno to ERANGE,
fail if we care about those side-effects. */
&& (flag_trapping_math || flag_errno_math))
return false;
/* Similarly for nextafter (0, 1) raising underflow. */
else if (flag_trapping_math
&& arg0->cl == rvc_zero
&& result->cl != rvc_zero)
return false;
real_convert (result, format, result);
return true;
}
/* Try to evaluate:
*RESULT = ldexp (*ARG0, ARG1)
in format FORMAT. Return true on success. */
static bool
fold_const_builtin_load_exponent (real_value *result, const real_value *arg0,
const wide_int_ref &arg1,
const real_format *format)
{
/* Bound the maximum adjustment to twice the range of the
mode's valid exponents. Use abs to ensure the range is
positive as a sanity check. */
int max_exp_adj = 2 * labs (format->emax - format->emin);
/* The requested adjustment must be inside this range. This
is a preliminary cap to avoid things like overflow, we
may still fail to compute the result for other reasons. */
if (wi::les_p (arg1, -max_exp_adj) || wi::ges_p (arg1, max_exp_adj))
return false;
/* Don't perform operation if we honor signaling NaNs and
operand is a signaling NaN. */
if (!flag_unsafe_math_optimizations
&& flag_signaling_nans
&& REAL_VALUE_ISSIGNALING_NAN (*arg0))
return false;
REAL_VALUE_TYPE initial_result;
real_ldexp (&initial_result, arg0, arg1.to_shwi ());
/* Ensure we didn't overflow. */
if (real_isinf (&initial_result))
return false;
/* Only proceed if the target mode can hold the
resulting value. */
*result = real_value_truncate (format, initial_result);
return real_equal (&initial_result, result);
}
/* Fold a call to __builtin_nan or __builtin_nans with argument ARG and
return type TYPE. QUIET is true if a quiet rather than signalling
NaN is required. */
static tree
fold_const_builtin_nan (tree type, tree arg, bool quiet)
{
REAL_VALUE_TYPE real;
const char *str = c_getstr (arg);
if (str && real_nan (&real, str, quiet, TYPE_MODE (type)))
return build_real (type, real);
return NULL_TREE;
}
/* Fold a call to IFN_REDUC_<CODE> (ARG), returning a value of type TYPE. */
static tree
fold_const_reduction (tree type, tree arg, tree_code code)
{
unsigned HOST_WIDE_INT nelts;
if (TREE_CODE (arg) != VECTOR_CST
|| !VECTOR_CST_NELTS (arg).is_constant (&nelts))
return NULL_TREE;
tree res = VECTOR_CST_ELT (arg, 0);
for (unsigned HOST_WIDE_INT i = 1; i < nelts; i++)
{
res = const_binop (code, type, res, VECTOR_CST_ELT (arg, i));
if (res == NULL_TREE || !CONSTANT_CLASS_P (res))
return NULL_TREE;
}
return res;
}
/* Fold a call to IFN_VEC_CONVERT (ARG) returning TYPE. */
static tree
fold_const_vec_convert (tree ret_type, tree arg)
{
enum tree_code code = NOP_EXPR;
tree arg_type = TREE_TYPE (arg);
if (TREE_CODE (arg) != VECTOR_CST)
return NULL_TREE;
gcc_checking_assert (VECTOR_TYPE_P (ret_type) && VECTOR_TYPE_P (arg_type));
if (INTEGRAL_TYPE_P (TREE_TYPE (ret_type))
&& SCALAR_FLOAT_TYPE_P (TREE_TYPE (arg_type)))
code = FIX_TRUNC_EXPR;
else if (INTEGRAL_TYPE_P (TREE_TYPE (arg_type))
&& SCALAR_FLOAT_TYPE_P (TREE_TYPE (ret_type)))
code = FLOAT_EXPR;
/* We can't handle steps directly when extending, since the
values need to wrap at the original precision first. */
bool step_ok_p
= (INTEGRAL_TYPE_P (TREE_TYPE (ret_type))
&& INTEGRAL_TYPE_P (TREE_TYPE (arg_type))
&& (TYPE_PRECISION (TREE_TYPE (ret_type))
<= TYPE_PRECISION (TREE_TYPE (arg_type))));
tree_vector_builder elts;
if (!elts.new_unary_operation (ret_type, arg, step_ok_p))
return NULL_TREE;
unsigned int count = elts.encoded_nelts ();
for (unsigned int i = 0; i < count; ++i)
{
tree elt = fold_unary (code, TREE_TYPE (ret_type),
VECTOR_CST_ELT (arg, i));
if (elt == NULL_TREE || !CONSTANT_CLASS_P (elt))
return NULL_TREE;
elts.quick_push (elt);
}
return elts.build ();
}
/* Try to evaluate:
IFN_WHILE_ULT (ARG0, ARG1, (TYPE) { ... })
Return the value on success and null on failure. */
static tree
fold_while_ult (tree type, poly_uint64 arg0, poly_uint64 arg1)
{
if (known_ge (arg0, arg1))
return build_zero_cst (type);
if (maybe_ge (arg0, arg1))
return NULL_TREE;
poly_uint64 diff = arg1 - arg0;
poly_uint64 nelts = TYPE_VECTOR_SUBPARTS (type);
if (known_ge (diff, nelts))
return build_all_ones_cst (type);
unsigned HOST_WIDE_INT const_diff;
if (known_le (diff, nelts) && diff.is_constant (&const_diff))
{
tree minus_one = build_minus_one_cst (TREE_TYPE (type));
tree zero = build_zero_cst (TREE_TYPE (type));
return build_vector_a_then_b (type, const_diff, minus_one, zero);
}
return NULL_TREE;
}
/* Try to evaluate:
*RESULT = FN (*ARG)
in format FORMAT. Return true on success. */
static bool
fold_const_call_ss (real_value *result, combined_fn fn,
const real_value *arg, const real_format *format)
{
switch (fn)
{
CASE_CFN_SQRT:
CASE_CFN_SQRT_FN:
return (real_compare (GE_EXPR, arg, &dconst0)
&& do_mpfr_arg1 (result, mpfr_sqrt, arg, format));
CASE_CFN_CBRT:
return do_mpfr_arg1 (result, mpfr_cbrt, arg, format);
CASE_CFN_ASIN:
return (real_compare (GE_EXPR, arg, &dconstm1)
&& real_compare (LE_EXPR, arg, &dconst1)
&& do_mpfr_arg1 (result, mpfr_asin, arg, format));
CASE_CFN_ACOS:
return (real_compare (GE_EXPR, arg, &dconstm1)
&& real_compare (LE_EXPR, arg, &dconst1)
&& do_mpfr_arg1 (result, mpfr_acos, arg, format));
CASE_CFN_ATAN:
return do_mpfr_arg1 (result, mpfr_atan, arg, format);
CASE_CFN_ASINH:
return do_mpfr_arg1 (result, mpfr_asinh, arg, format);
CASE_CFN_ACOSH:
return (real_compare (GE_EXPR, arg, &dconst1)
&& do_mpfr_arg1 (result, mpfr_acosh, arg, format));
CASE_CFN_ATANH:
return (real_compare (GE_EXPR, arg, &dconstm1)
&& real_compare (LE_EXPR, arg, &dconst1)
&& do_mpfr_arg1 (result, mpfr_atanh, arg, format));
CASE_CFN_SIN:
return do_mpfr_arg1 (result, mpfr_sin, arg, format);
CASE_CFN_COS:
return do_mpfr_arg1 (result, mpfr_cos, arg, format);
CASE_CFN_TAN:
return do_mpfr_arg1 (result, mpfr_tan, arg, format);
CASE_CFN_SINH:
return do_mpfr_arg1 (result, mpfr_sinh, arg, format);
CASE_CFN_COSH:
return do_mpfr_arg1 (result, mpfr_cosh, arg, format);
CASE_CFN_TANH:
return do_mpfr_arg1 (result, mpfr_tanh, arg, format);
CASE_CFN_ERF:
return do_mpfr_arg1 (result, mpfr_erf, arg, format);
CASE_CFN_ERFC:
return do_mpfr_arg1 (result, mpfr_erfc, arg, format);
CASE_CFN_TGAMMA:
return do_mpfr_arg1 (result, mpfr_gamma, arg, format);
CASE_CFN_EXP:
return do_mpfr_arg1 (result, mpfr_exp, arg, format);
CASE_CFN_EXP2:
return do_mpfr_arg1 (result, mpfr_exp2, arg, format);
CASE_CFN_EXP10:
CASE_CFN_POW10:
return do_mpfr_arg1 (result, mpfr_exp10, arg, format);
CASE_CFN_EXPM1:
return do_mpfr_arg1 (result, mpfr_expm1, arg, format);
CASE_CFN_LOG:
return (real_compare (GT_EXPR, arg, &dconst0)
&& do_mpfr_arg1 (result, mpfr_log, arg, format));
CASE_CFN_LOG2:
return (real_compare (GT_EXPR, arg, &dconst0)
&& do_mpfr_arg1 (result, mpfr_log2, arg, format));
CASE_CFN_LOG10:
return (real_compare (GT_EXPR, arg, &dconst0)
&& do_mpfr_arg1 (result, mpfr_log10, arg, format));
CASE_CFN_LOG1P:
return (real_compare (GT_EXPR, arg, &dconstm1)
&& do_mpfr_arg1 (result, mpfr_log1p, arg, format));
CASE_CFN_J0:
return do_mpfr_arg1 (result, mpfr_j0, arg, format);
CASE_CFN_J1:
return do_mpfr_arg1 (result, mpfr_j1, arg, format);
CASE_CFN_Y0:
return (real_compare (GT_EXPR, arg, &dconst0)
&& do_mpfr_arg1 (result, mpfr_y0, arg, format));
CASE_CFN_Y1:
return (real_compare (GT_EXPR, arg, &dconst0)
&& do_mpfr_arg1 (result, mpfr_y1, arg, format));
CASE_CFN_FLOOR:
CASE_CFN_FLOOR_FN:
if (!REAL_VALUE_ISSIGNALING_NAN (*arg))
{
real_floor (result, format, arg);
return true;
}
return false;
CASE_CFN_CEIL:
CASE_CFN_CEIL_FN:
if (!REAL_VALUE_ISSIGNALING_NAN (*arg))
{
real_ceil (result, format, arg);
return true;
}
return false;
CASE_CFN_TRUNC:
CASE_CFN_TRUNC_FN:
if (!REAL_VALUE_ISSIGNALING_NAN (*arg))
{
real_trunc (result, format, arg);
return true;
}
return false;
CASE_CFN_ROUND:
CASE_CFN_ROUND_FN:
if (!REAL_VALUE_ISSIGNALING_NAN (*arg))
{
real_round (result, format, arg);
return true;
}
return false;
CASE_CFN_ROUNDEVEN:
CASE_CFN_ROUNDEVEN_FN:
if (!REAL_VALUE_ISSIGNALING_NAN (*arg))
{
real_roundeven (result, format, arg);
return true;
}
return false;
CASE_CFN_LOGB:
return fold_const_logb (result, arg, format);
CASE_CFN_SIGNIFICAND:
return fold_const_significand (result, arg, format);
default:
return false;
}
}
/* Try to evaluate:
*RESULT = FN (*ARG)
where FORMAT is the format of ARG and PRECISION is the number of
significant bits in the result. Return true on success. */
static bool
fold_const_call_ss (wide_int *result, combined_fn fn,
const real_value *arg, unsigned int precision,
const real_format *format)
{
switch (fn)
{
CASE_CFN_SIGNBIT:
if (real_isneg (arg))
*result = wi::one (precision);
else
*result = wi::zero (precision);
return true;
CASE_CFN_ILOGB:
/* For ilogb we don't know FP_ILOGB0, so only handle normal values.
Proceed iff radix == 2. In GCC, normalized significands are in
the range [0.5, 1.0). We want the exponent as if they were
[1.0, 2.0) so get the exponent and subtract 1. */
if (arg->cl == rvc_normal && format->b == 2)
{
*result = wi::shwi (REAL_EXP (arg) - 1, precision);
return true;
}
return false;
CASE_CFN_ICEIL:
CASE_CFN_LCEIL:
CASE_CFN_LLCEIL:
return fold_const_conversion (result, real_ceil, arg,
precision, format);
CASE_CFN_LFLOOR:
CASE_CFN_IFLOOR:
CASE_CFN_LLFLOOR:
return fold_const_conversion (result, real_floor, arg,
precision, format);
CASE_CFN_IROUND:
CASE_CFN_LROUND:
CASE_CFN_LLROUND:
return fold_const_conversion (result, real_round, arg,
precision, format);
CASE_CFN_IRINT:
CASE_CFN_LRINT:
CASE_CFN_LLRINT:
/* Not yet folded to a constant. */
return false;
CASE_CFN_FINITE:
case CFN_BUILT_IN_FINITED32:
case CFN_BUILT_IN_FINITED64:
case CFN_BUILT_IN_FINITED128:
case CFN_BUILT_IN_ISFINITE:
*result = wi::shwi (real_isfinite (arg) ? 1 : 0, precision);
return true;
CASE_CFN_ISINF:
case CFN_BUILT_IN_ISINFD32:
case CFN_BUILT_IN_ISINFD64:
case CFN_BUILT_IN_ISINFD128:
if (real_isinf (arg))
*result = wi::shwi (arg->sign ? -1 : 1, precision);
else
*result = wi::shwi (0, precision);
return true;
CASE_CFN_ISNAN:
case CFN_BUILT_IN_ISNAND32:
case CFN_BUILT_IN_ISNAND64:
case CFN_BUILT_IN_ISNAND128:
*result = wi::shwi (real_isnan (arg) ? 1 : 0, precision);
return true;
default:
return false;
}
}
/* Try to evaluate:
*RESULT = FN (ARG)
where ARG_TYPE is the type of ARG and PRECISION is the number of bits
in the result. Return true on success. */
static bool
fold_const_call_ss (wide_int *result, combined_fn fn, const wide_int_ref &arg,
unsigned int precision, tree arg_type)
{
switch (fn)
{
CASE_CFN_FFS:
*result = wi::shwi (wi::ffs (arg), precision);
return true;
CASE_CFN_CLZ:
{
int tmp;
if (wi::ne_p (arg, 0))
tmp = wi::clz (arg);
else if (!CLZ_DEFINED_VALUE_AT_ZERO (SCALAR_INT_TYPE_MODE (arg_type),
tmp))
tmp = TYPE_PRECISION (arg_type);
*result = wi::shwi (tmp, precision);
return true;
}
CASE_CFN_CTZ:
{
int tmp;
if (wi::ne_p (arg, 0))
tmp = wi::ctz (arg);
else if (!CTZ_DEFINED_VALUE_AT_ZERO (SCALAR_INT_TYPE_MODE (arg_type),
tmp))
tmp = TYPE_PRECISION (arg_type);
*result = wi::shwi (tmp, precision);
return true;
}
CASE_CFN_CLRSB:
*result = wi::shwi (wi::clrsb (arg), precision);
return true;
CASE_CFN_POPCOUNT:
*result = wi::shwi (wi::popcount (arg), precision);
return true;
CASE_CFN_PARITY:
*result = wi::shwi (wi::parity (arg), precision);
return true;
case CFN_BUILT_IN_BSWAP16:
case CFN_BUILT_IN_BSWAP32:
case CFN_BUILT_IN_BSWAP64:
case CFN_BUILT_IN_BSWAP128:
*result = wide_int::from (arg, precision, TYPE_SIGN (arg_type)).bswap ();
return true;
default:
return false;
}
}
/* Try to evaluate:
RESULT = FN (*ARG)
where FORMAT is the format of ARG and of the real and imaginary parts
of RESULT, passed as RESULT_REAL and RESULT_IMAG respectively. Return
true on success. */
static bool
fold_const_call_cs (real_value *result_real, real_value *result_imag,
combined_fn fn, const real_value *arg,
const real_format *format)
{
switch (fn)
{
CASE_CFN_CEXPI:
/* cexpi(x+yi) = cos(x)+sin(y)*i. */
return do_mpfr_sincos (result_imag, result_real, arg, format);
default:
return false;
}
}
/* Try to evaluate:
*RESULT = fn (ARG)
where FORMAT is the format of RESULT and of the real and imaginary parts
of ARG, passed as ARG_REAL and ARG_IMAG respectively. Return true on
success. */
static bool
fold_const_call_sc (real_value *result, combined_fn fn,
const real_value *arg_real, const real_value *arg_imag,
const real_format *format)
{
switch (fn)
{
CASE_CFN_CABS:
return do_mpfr_arg2 (result, mpfr_hypot, arg_real, arg_imag, format);
default:
return false;
}
}
/* Try to evaluate:
RESULT = fn (ARG)
where FORMAT is the format of the real and imaginary parts of RESULT
(RESULT_REAL and RESULT_IMAG) and of ARG (ARG_REAL and ARG_IMAG).
Return true on success. */
static bool
fold_const_call_cc (real_value *result_real, real_value *result_imag,
combined_fn fn, const real_value *arg_real,
const real_value *arg_imag, const real_format *format)
{
switch (fn)
{
CASE_CFN_CCOS:
return do_mpc_arg1 (result_real, result_imag, mpc_cos,
arg_real, arg_imag, format);
CASE_CFN_CCOSH:
return do_mpc_arg1 (result_real, result_imag, mpc_cosh,
arg_real, arg_imag, format);
CASE_CFN_CPROJ:
if (real_isinf (arg_real) || real_isinf (arg_imag))
{
real_inf (result_real);
*result_imag = dconst0;
result_imag->sign = arg_imag->sign;
}
else
{
*result_real = *arg_real;
*result_imag = *arg_imag;
}
return true;
CASE_CFN_CSIN:
return do_mpc_arg1 (result_real, result_imag, mpc_sin,
arg_real, arg_imag, format);
CASE_CFN_CSINH:
return do_mpc_arg1 (result_real, result_imag, mpc_sinh,
arg_real, arg_imag, format);
CASE_CFN_CTAN:
return do_mpc_arg1 (result_real, result_imag, mpc_tan,
arg_real, arg_imag, format);
CASE_CFN_CTANH:
return do_mpc_arg1 (result_real, result_imag, mpc_tanh,
arg_real, arg_imag, format);
CASE_CFN_CLOG:
return do_mpc_arg1 (result_real, result_imag, mpc_log,
arg_real, arg_imag, format);
CASE_CFN_CSQRT:
return do_mpc_arg1 (result_real, result_imag, mpc_sqrt,
arg_real, arg_imag, format);
CASE_CFN_CASIN:
return do_mpc_arg1 (result_real, result_imag, mpc_asin,
arg_real, arg_imag, format);
CASE_CFN_CACOS:
return do_mpc_arg1 (result_real, result_imag, mpc_acos,
arg_real, arg_imag, format);
CASE_CFN_CATAN:
return do_mpc_arg1 (result_real, result_imag, mpc_atan,
arg_real, arg_imag, format);
CASE_CFN_CASINH:
return do_mpc_arg1 (result_real, result_imag, mpc_asinh,
arg_real, arg_imag, format);
CASE_CFN_CACOSH:
return do_mpc_arg1 (result_real, result_imag, mpc_acosh,
arg_real, arg_imag, format);
CASE_CFN_CATANH:
return do_mpc_arg1 (result_real, result_imag, mpc_atanh,
arg_real, arg_imag, format);
CASE_CFN_CEXP:
return do_mpc_arg1 (result_real, result_imag, mpc_exp,
arg_real, arg_imag, format);
default:
return false;
}
}
/* Subroutine of fold_const_call, with the same interface. Handle cases
where the arguments and result are numerical. */
static tree
fold_const_call_1 (combined_fn fn, tree type, tree arg)
{
machine_mode mode = TYPE_MODE (type);
machine_mode arg_mode = TYPE_MODE (TREE_TYPE (arg));
if (integer_cst_p (arg))
{
if (SCALAR_INT_MODE_P (mode))
{
wide_int result;
if (fold_const_call_ss (&result, fn, wi::to_wide (arg),
TYPE_PRECISION (type), TREE_TYPE (arg)))
return wide_int_to_tree (type, result);
}
return NULL_TREE;
}
if (real_cst_p (arg))
{
gcc_checking_assert (SCALAR_FLOAT_MODE_P (arg_mode));
if (mode == arg_mode)
{
/* real -> real. */
REAL_VALUE_TYPE result;
if (fold_const_call_ss (&result, fn, TREE_REAL_CST_PTR (arg),
REAL_MODE_FORMAT (mode)))
return build_real (type, result);
}
else if (COMPLEX_MODE_P (mode)
&& GET_MODE_INNER (mode) == arg_mode)
{
/* real -> complex real. */
REAL_VALUE_TYPE result_real, result_imag;
if (fold_const_call_cs (&result_real, &result_imag, fn,
TREE_REAL_CST_PTR (arg),
REAL_MODE_FORMAT (arg_mode)))
return build_complex (type,
build_real (TREE_TYPE (type), result_real),
build_real (TREE_TYPE (type), result_imag));
}
else if (INTEGRAL_TYPE_P (type))
{
/* real -> int. */
wide_int result;
if (fold_const_call_ss (&result, fn,
TREE_REAL_CST_PTR (arg),
TYPE_PRECISION (type),
REAL_MODE_FORMAT (arg_mode)))
return wide_int_to_tree (type, result);
}
return NULL_TREE;
}
if (complex_cst_p (arg))
{
gcc_checking_assert (COMPLEX_MODE_P (arg_mode));
machine_mode inner_mode = GET_MODE_INNER (arg_mode);
tree argr = TREE_REALPART (arg);
tree argi = TREE_IMAGPART (arg);
if (mode == arg_mode
&& real_cst_p (argr)
&& real_cst_p (argi))
{
/* complex real -> complex real. */
REAL_VALUE_TYPE result_real, result_imag;
if (fold_const_call_cc (&result_real, &result_imag, fn,
TREE_REAL_CST_PTR (argr),
TREE_REAL_CST_PTR (argi),
REAL_MODE_FORMAT (inner_mode)))
return build_complex (type,
build_real (TREE_TYPE (type), result_real),
build_real (TREE_TYPE (type), result_imag));
}
if (mode == inner_mode
&& real_cst_p (argr)
&& real_cst_p (argi))
{
/* complex real -> real. */
REAL_VALUE_TYPE result;
if (fold_const_call_sc (&result, fn,
TREE_REAL_CST_PTR (argr),
TREE_REAL_CST_PTR (argi),
REAL_MODE_FORMAT (inner_mode)))
return build_real (type, result);
}
return NULL_TREE;
}
return NULL_TREE;
}
/* Try to fold FN (ARG) to a constant. Return the constant on success,
otherwise return null. TYPE is the type of the return value. */
tree
fold_const_call (combined_fn fn, tree type, tree arg)
{
switch (fn)
{
case CFN_BUILT_IN_STRLEN:
if (const char *str = c_getstr (arg))
return build_int_cst (type, strlen (str));
return NULL_TREE;
CASE_CFN_NAN:
CASE_FLT_FN_FLOATN_NX (CFN_BUILT_IN_NAN):
case CFN_BUILT_IN_NAND32:
case CFN_BUILT_IN_NAND64:
case CFN_BUILT_IN_NAND128:
return fold_const_builtin_nan (type, arg, true);
CASE_CFN_NANS:
CASE_FLT_FN_FLOATN_NX (CFN_BUILT_IN_NANS):
return fold_const_builtin_nan (type, arg, false);
case CFN_REDUC_PLUS:
return fold_const_reduction (type, arg, PLUS_EXPR);
case CFN_REDUC_MAX:
return fold_const_reduction (type, arg, MAX_EXPR);
case CFN_REDUC_MIN:
return fold_const_reduction (type, arg, MIN_EXPR);
case CFN_REDUC_AND:
return fold_const_reduction (type, arg, BIT_AND_EXPR);
case CFN_REDUC_IOR:
return fold_const_reduction (type, arg, BIT_IOR_EXPR);
case CFN_REDUC_XOR:
return fold_const_reduction (type, arg, BIT_XOR_EXPR);
case CFN_VEC_CONVERT:
return fold_const_vec_convert (type, arg);
default:
return fold_const_call_1 (fn, type, arg);
}
}
/* Fold a call to IFN_FOLD_LEFT_<CODE> (ARG0, ARG1), returning a value
of type TYPE. */
static tree
fold_const_fold_left (tree type, tree arg0, tree arg1, tree_code code)
{
if (TREE_CODE (arg1) != VECTOR_CST)
return NULL_TREE;
unsigned HOST_WIDE_INT nelts;
if (!VECTOR_CST_NELTS (arg1).is_constant (&nelts))
return NULL_TREE;
for (unsigned HOST_WIDE_INT i = 0; i < nelts; i++)
{
arg0 = const_binop (code, type, arg0, VECTOR_CST_ELT (arg1, i));
if (arg0 == NULL_TREE || !CONSTANT_CLASS_P (arg0))
return NULL_TREE;
}
return arg0;
}
/* Try to evaluate:
*RESULT = FN (*ARG0, *ARG1)
in format FORMAT. Return true on success. */
static bool
fold_const_call_sss (real_value *result, combined_fn fn,
const real_value *arg0, const real_value *arg1,
const real_format *format)
{
switch (fn)
{
CASE_CFN_DREM:
CASE_CFN_REMAINDER:
return do_mpfr_arg2 (result, mpfr_remainder, arg0, arg1, format);
CASE_CFN_ATAN2:
return do_mpfr_arg2 (result, mpfr_atan2, arg0, arg1, format);
CASE_CFN_FDIM:
return do_mpfr_arg2 (result, mpfr_dim, arg0, arg1, format);
CASE_CFN_HYPOT:
return do_mpfr_arg2 (result, mpfr_hypot, arg0, arg1, format);
CASE_CFN_COPYSIGN:
CASE_CFN_COPYSIGN_FN:
*result = *arg0;
real_copysign (result, arg1);
return true;
CASE_CFN_FMIN:
CASE_CFN_FMIN_FN:
return do_mpfr_arg2 (result, mpfr_min, arg0, arg1, format);
CASE_CFN_FMAX:
CASE_CFN_FMAX_FN:
return do_mpfr_arg2 (result, mpfr_max, arg0, arg1, format);
CASE_CFN_POW:
return fold_const_pow (result, arg0, arg1, format);
CASE_CFN_NEXTAFTER:
CASE_CFN_NEXTTOWARD:
return fold_const_nextafter (result, arg0, arg1, format);
default:
return false;
}
}
/* Try to evaluate:
*RESULT = FN (*ARG0, ARG1)
where FORMAT is the format of *RESULT and *ARG0. Return true on
success. */
static bool
fold_const_call_sss (real_value *result, combined_fn fn,
const real_value *arg0, const wide_int_ref &arg1,
const real_format *format)
{
switch (fn)
{
CASE_CFN_LDEXP:
return fold_const_builtin_load_exponent (result, arg0, arg1, format);
CASE_CFN_SCALBN:
CASE_CFN_SCALBLN:
return (format->b == 2
&& fold_const_builtin_load_exponent (result, arg0, arg1,
format));
CASE_CFN_POWI:
/* Avoid the folding if flag_signaling_nans is on and
operand is a signaling NaN. */
if (!flag_unsafe_math_optimizations
&& flag_signaling_nans
&& REAL_VALUE_ISSIGNALING_NAN (*arg0))
return false;
real_powi (result, format, arg0, arg1.to_shwi ());
return true;
default:
return false;
}
}
/* Try to evaluate:
*RESULT = FN (ARG0, *ARG1)
where FORMAT is the format of *RESULT and *ARG1. Return true on
success. */
static bool
fold_const_call_sss (real_value *result, combined_fn fn,
const wide_int_ref &arg0, const real_value *arg1,
const real_format *format)
{
switch (fn)
{
CASE_CFN_JN:
return do_mpfr_arg2 (result, mpfr_jn, arg0, arg1, format);
CASE_CFN_YN:
return (real_compare (GT_EXPR, arg1, &dconst0)
&& do_mpfr_arg2 (result, mpfr_yn, arg0, arg1, format));
default:
return false;
}
}
/* Try to evaluate:
RESULT = fn (ARG0, ARG1)
where FORMAT is the format of the real and imaginary parts of RESULT
(RESULT_REAL and RESULT_IMAG), of ARG0 (ARG0_REAL and ARG0_IMAG)
and of ARG1 (ARG1_REAL and ARG1_IMAG). Return true on success. */
static bool
fold_const_call_ccc (real_value *result_real, real_value *result_imag,
combined_fn fn, const real_value *arg0_real,
const real_value *arg0_imag, const real_value *arg1_real,
const real_value *arg1_imag, const real_format *format)
{
switch (fn)
{
CASE_CFN_CPOW:
return do_mpc_arg2 (result_real, result_imag, mpc_pow,
arg0_real, arg0_imag, arg1_real, arg1_imag, format);
default:
return false;
}
}
/* Subroutine of fold_const_call, with the same interface. Handle cases
where the arguments and result are numerical. */
static tree
fold_const_call_1 (combined_fn fn, tree type, tree arg0, tree arg1)
{
machine_mode mode = TYPE_MODE (type);
machine_mode arg0_mode = TYPE_MODE (TREE_TYPE (arg0));
machine_mode arg1_mode = TYPE_MODE (TREE_TYPE (arg1));
if (mode == arg0_mode
&& real_cst_p (arg0)
&& real_cst_p (arg1))
{
gcc_checking_assert (SCALAR_FLOAT_MODE_P (arg0_mode));
REAL_VALUE_TYPE result;
if (arg0_mode == arg1_mode)
{
/* real, real -> real. */
if (fold_const_call_sss (&result, fn, TREE_REAL_CST_PTR (arg0),
TREE_REAL_CST_PTR (arg1),
REAL_MODE_FORMAT (mode)))
return build_real (type, result);
}
else if (arg1_mode == TYPE_MODE (long_double_type_node))
switch (fn)
{
CASE_CFN_NEXTTOWARD:
/* real, long double -> real. */
if (fold_const_call_sss (&result, fn, TREE_REAL_CST_PTR (arg0),
TREE_REAL_CST_PTR (arg1),
REAL_MODE_FORMAT (mode)))
return build_real (type, result);
break;
default:
break;
}
return NULL_TREE;
}
if (real_cst_p (arg0)
&& integer_cst_p (arg1))
{
gcc_checking_assert (SCALAR_FLOAT_MODE_P (arg0_mode));
if (mode == arg0_mode)
{
/* real, int -> real. */
REAL_VALUE_TYPE result;
if (fold_const_call_sss (&result, fn, TREE_REAL_CST_PTR (arg0),
wi::to_wide (arg1),
REAL_MODE_FORMAT (mode)))
return build_real (type, result);
}
return NULL_TREE;
}
if (integer_cst_p (arg0)
&& real_cst_p (arg1))
{
gcc_checking_assert (SCALAR_FLOAT_MODE_P (arg1_mode));
if (mode == arg1_mode)
{
/* int, real -> real. */
REAL_VALUE_TYPE result;
if (fold_const_call_sss (&result, fn, wi::to_wide (arg0),
TREE_REAL_CST_PTR (arg1),
REAL_MODE_FORMAT (mode)))
return build_real (type, result);
}
return NULL_TREE;
}
if (arg0_mode == arg1_mode
&& complex_cst_p (arg0)
&& complex_cst_p (arg1))
{
gcc_checking_assert (COMPLEX_MODE_P (arg0_mode));
machine_mode inner_mode = GET_MODE_INNER (arg0_mode);
tree arg0r = TREE_REALPART (arg0);
tree arg0i = TREE_IMAGPART (arg0);
tree arg1r = TREE_REALPART (arg1);
tree arg1i = TREE_IMAGPART (arg1);
if (mode == arg0_mode
&& real_cst_p (arg0r)
&& real_cst_p (arg0i)
&& real_cst_p (arg1r)
&& real_cst_p (arg1i))
{
/* complex real, complex real -> complex real. */
REAL_VALUE_TYPE result_real, result_imag;
if (fold_const_call_ccc (&result_real, &result_imag, fn,
TREE_REAL_CST_PTR (arg0r),
TREE_REAL_CST_PTR (arg0i),
TREE_REAL_CST_PTR (arg1r),
TREE_REAL_CST_PTR (arg1i),
REAL_MODE_FORMAT (inner_mode)))
return build_complex (type,
build_real (TREE_TYPE (type), result_real),
build_real (TREE_TYPE (type), result_imag));
}
return NULL_TREE;
}
return NULL_TREE;
}
/* Try to fold FN (ARG0, ARG1) to a constant. Return the constant on success,
otherwise return null. TYPE is the type of the return value. */
tree
fold_const_call (combined_fn fn, tree type, tree arg0, tree arg1)
{
const char *p0, *p1;
char c;
switch (fn)
{
case CFN_BUILT_IN_STRSPN:
if ((p0 = c_getstr (arg0)) && (p1 = c_getstr (arg1)))
return build_int_cst (type, strspn (p0, p1));
return NULL_TREE;
case CFN_BUILT_IN_STRCSPN:
if ((p0 = c_getstr (arg0)) && (p1 = c_getstr (arg1)))
return build_int_cst (type, strcspn (p0, p1));
return NULL_TREE;
case CFN_BUILT_IN_STRCMP:
if ((p0 = c_getstr (arg0)) && (p1 = c_getstr (arg1)))
return build_cmp_result (type, strcmp (p0, p1));
return NULL_TREE;
case CFN_BUILT_IN_STRCASECMP:
if ((p0 = c_getstr (arg0)) && (p1 = c_getstr (arg1)))
{
int r = strcmp (p0, p1);
if (r == 0)
return build_cmp_result (type, r);
}
return NULL_TREE;
case CFN_BUILT_IN_INDEX:
case CFN_BUILT_IN_STRCHR:
if ((p0 = c_getstr (arg0)) && target_char_cst_p (arg1, &c))
{
const char *r = strchr (p0, c);
if (r == NULL)
return build_int_cst (type, 0);
return fold_convert (type,
fold_build_pointer_plus_hwi (arg0, r - p0));
}
return NULL_TREE;
case CFN_BUILT_IN_RINDEX:
case CFN_BUILT_IN_STRRCHR:
if ((p0 = c_getstr (arg0)) && target_char_cst_p (arg1, &c))
{
const char *r = strrchr (p0, c);
if (r == NULL)
return build_int_cst (type, 0);
return fold_convert (type,
fold_build_pointer_plus_hwi (arg0, r - p0));
}
return NULL_TREE;
case CFN_BUILT_IN_STRSTR:
if ((p1 = c_getstr (arg1)))
{
if ((p0 = c_getstr (arg0)))
{
const char *r = strstr (p0, p1);
if (r == NULL)
return build_int_cst (type, 0);
return fold_convert (type,
fold_build_pointer_plus_hwi (arg0, r - p0));
}
if (*p1 == '\0')
return fold_convert (type, arg0);
}
return NULL_TREE;
case CFN_FOLD_LEFT_PLUS:
return fold_const_fold_left (type, arg0, arg1, PLUS_EXPR);
default:
return fold_const_call_1 (fn, type, arg0, arg1);
}
}
/* Try to evaluate:
*RESULT = FN (*ARG0, *ARG1, *ARG2)
in format FORMAT. Return true on success. */
static bool
fold_const_call_ssss (real_value *result, combined_fn fn,
const real_value *arg0, const real_value *arg1,
const real_value *arg2, const real_format *format)
{
switch (fn)
{
CASE_CFN_FMA:
CASE_CFN_FMA_FN:
return do_mpfr_arg3 (result, mpfr_fma, arg0, arg1, arg2, format);
case CFN_FMS:
{
real_value new_arg2 = real_value_negate (arg2);
return do_mpfr_arg3 (result, mpfr_fma, arg0, arg1, &new_arg2, format);
}
case CFN_FNMA:
{
real_value new_arg0 = real_value_negate (arg0);
return do_mpfr_arg3 (result, mpfr_fma, &new_arg0, arg1, arg2, format);
}
case CFN_FNMS:
{
real_value new_arg0 = real_value_negate (arg0);
real_value new_arg2 = real_value_negate (arg2);
return do_mpfr_arg3 (result, mpfr_fma, &new_arg0, arg1,
&new_arg2, format);
}
default:
return false;
}
}
/* Subroutine of fold_const_call, with the same interface. Handle cases
where the arguments and result are numerical. */
static tree
fold_const_call_1 (combined_fn fn, tree type, tree arg0, tree arg1, tree arg2)
{
machine_mode mode = TYPE_MODE (type);
machine_mode arg0_mode = TYPE_MODE (TREE_TYPE (arg0));
machine_mode arg1_mode = TYPE_MODE (TREE_TYPE (arg1));
machine_mode arg2_mode = TYPE_MODE (TREE_TYPE (arg2));
if (arg0_mode == arg1_mode
&& arg0_mode == arg2_mode
&& real_cst_p (arg0)
&& real_cst_p (arg1)
&& real_cst_p (arg2))
{
gcc_checking_assert (SCALAR_FLOAT_MODE_P (arg0_mode));
if (mode == arg0_mode)
{
/* real, real, real -> real. */
REAL_VALUE_TYPE result;
if (fold_const_call_ssss (&result, fn, TREE_REAL_CST_PTR (arg0),
TREE_REAL_CST_PTR (arg1),
TREE_REAL_CST_PTR (arg2),
REAL_MODE_FORMAT (mode)))
return build_real (type, result);
}
return NULL_TREE;
}
return NULL_TREE;
}
/* Try to fold FN (ARG0, ARG1, ARG2) to a constant. Return the constant on
success, otherwise return null. TYPE is the type of the return value. */
tree
fold_const_call (combined_fn fn, tree type, tree arg0, tree arg1, tree arg2)
{
const char *p0, *p1;
char c;
unsigned HOST_WIDE_INT s0, s1;
size_t s2 = 0;
switch (fn)
{
case CFN_BUILT_IN_STRNCMP:
if (!host_size_t_cst_p (arg2, &s2))
return NULL_TREE;
if (s2 == 0
&& !TREE_SIDE_EFFECTS (arg0)
&& !TREE_SIDE_EFFECTS (arg1))
return build_int_cst (type, 0);
else if ((p0 = c_getstr (arg0)) && (p1 = c_getstr (arg1)))
return build_int_cst (type, strncmp (p0, p1, s2));
return NULL_TREE;
case CFN_BUILT_IN_STRNCASECMP:
if (!host_size_t_cst_p (arg2, &s2))
return NULL_TREE;
if (s2 == 0
&& !TREE_SIDE_EFFECTS (arg0)
&& !TREE_SIDE_EFFECTS (arg1))
return build_int_cst (type, 0);
else if ((p0 = c_getstr (arg0))
&& (p1 = c_getstr (arg1))
&& strncmp (p0, p1, s2) == 0)
return build_int_cst (type, 0);
return NULL_TREE;
case CFN_BUILT_IN_BCMP:
case CFN_BUILT_IN_MEMCMP:
if (!host_size_t_cst_p (arg2, &s2))
return NULL_TREE;
if (s2 == 0
&& !TREE_SIDE_EFFECTS (arg0)
&& !TREE_SIDE_EFFECTS (arg1))
return build_int_cst (type, 0);
if ((p0 = c_getstr (arg0, &s0))
&& (p1 = c_getstr (arg1, &s1))
&& s2 <= s0
&& s2 <= s1)
return build_cmp_result (type, memcmp (p0, p1, s2));
return NULL_TREE;
case CFN_BUILT_IN_MEMCHR:
if (!host_size_t_cst_p (arg2, &s2))
return NULL_TREE;
if (s2 == 0
&& !TREE_SIDE_EFFECTS (arg0)
&& !TREE_SIDE_EFFECTS (arg1))
return build_int_cst (type, 0);
if ((p0 = c_getstr (arg0, &s0))
&& s2 <= s0
&& target_char_cst_p (arg1, &c))
{
const char *r = (const char *) memchr (p0, c, s2);
if (r == NULL)
return build_int_cst (type, 0);
return fold_convert (type,
fold_build_pointer_plus_hwi (arg0, r - p0));
}
return NULL_TREE;
case CFN_WHILE_ULT:
{
poly_uint64 parg0, parg1;
if (poly_int_tree_p (arg0, &parg0) && poly_int_tree_p (arg1, &parg1))
return fold_while_ult (type, parg0, parg1);
return NULL_TREE;
}
default:
return fold_const_call_1 (fn, type, arg0, arg1, arg2);
}
}