re PR middle-end/77925 (Add __builtin_issubnormal)

2017-06-08  Tamar Christina  <tamar.christina@arm.com>

	PR middle-end/77925
	PR middle-end/77926
	PR middle-end/66462

	* gcc/builtins.c (fold_builtin_fpclassify): Remove.
	(fold_builtin_interclass_mathfn): Remove.
	(expand_builtin): Add builtins to lowering list.
	(fold_builtin_n): Remove fold_builtin_varargs.
	(fold_builtin_varargs): Remove.
	* gcc/builtins.def (BUILT_IN_ISZERO, BUILT_IN_ISSUBNORMAL): New.
	* gcc/real.h (get_min_float): New.
	(real_format): Add is_ieee_compatible field.
	* gcc/real.c (get_min_float): New.
	(ieee_single_format): Set is_ieee_compatible flag.
	* gcc/gimple-low.c (lower_stm): Define BUILT_IN_FPCLASSIFY,
	CASE_FLT_FN (BUILT_IN_ISINF), BUILT_IN_ISINFD32, BUILT_IN_ISINFD64,
	BUILT_IN_ISINFD128, BUILT_IN_ISNAND32, BUILT_IN_ISNAND64,
	BUILT_IN_ISNAND128, BUILT_IN_ISNAN, BUILT_IN_ISNORMAL, BUILT_IN_ISZERO,
	BUILT_IN_ISSUBNORMAL, CASE_FLT_FN (BUILT_IN_FINITE), BUILT_IN_FINITED32
	BUILT_IN_FINITED64, BUILT_IN_FINITED128, BUILT_IN_ISFINITE.
	(lower_builtin_fpclassify, is_nan, is_normal, is_infinity): New.
	(is_zero, is_subnormal, is_finite, use_ieee_int_mode): Likewise.
	(lower_builtin_isnan, lower_builtin_isinfinite): Likewise.
	(lower_builtin_isnormal, lower_builtin_iszero): Likewise.
	(lower_builtin_issubnormal, lower_builtin_isfinite): Likewise.
	(emit_tree_cond, get_num_as_int, emit_tree_and_return_var): New.
	(mips_single_format): Likewise.
	(motorola_single_format): Likewise.
	(spu_single_format): Likewise.
	(ieee_double_format): Likewise.
	(mips_double_format): Likewise.
	(motorola_double_format): Likewise.
	(ieee_extended_motorola_format): Likewise.
	(ieee_extended_intel_128_format): Likewise.
	(ieee_extended_intel_96_round_53_format): Likewise.
	(ibm_extended_format): Likewise.
	(mips_extended_format): Likewise.
	(ieee_quad_format): Likewise.
	(mips_quad_format): Likewise.
	(vax_f_format): Likewise.
	(vax_d_format): Likewise.
	(vax_g_format): Likewise.
	(decimal_single_format): Likewise.
	(decimal_quad_format): Likewise.
	(iee_half_format): Likewise.
	(mips_single_format): Likewise.
	(arm_half_format): Likewise.
	(real_internal_format): Likewise.
	* gcc/doc/extend.texi: Add documentation for built-ins.
	* gcc/c/c-typeck.c (convert_arguments): Add BUILT_IN_ISZERO
	and BUILT_IN_ISSUBNORMAL.

gcc/testsuite/
2017-06-08  Tamar Christina  <tamar.christina@arm.com>

	* gcc.target/aarch64/builtin-fpclassify.c: New codegen test.
	* gcc.dg/fold-notunord.c: Removed.
	* gcc.dg/torture/floatn-tg-4.h: Add tests for iszero and issubnormal.
	* gcc.dg/torture/float128-tg-4.c: Likewise.
	* gcc.dg/torture/float128x-tg-4: Likewise.
	* gcc.dg/torture/float16-tg-4.c: Likewise.
	* gcc.dg/torture/float32-tg-4.c: Likewise.
	* gcc.dg/torture/float32x-tg-4.c: Likewise.
	* gcc.dg/torture/float64-tg-4.c: Likewise.
	* gcc.dg/torture/float64x-tg-4.c: Likewise.
	* gcc.dg/pr28796-1.c: Add -O2.
	* gcc.dg/builtins-43.c: Check lower instead of gimple.
	* gcc.dg/tg-tests.h: Add iszero and issubnormal.
	* gcc.dg/pr77925.c: Add to test safe cases.

From-SVN: r249005
This commit is contained in:
Tamar Christina 2017-06-08 07:38:42 +00:00 committed by Tamar Christina
parent fb4bc6ff6c
commit 94fc87ecdf
23 changed files with 1355 additions and 380 deletions

View File

@ -1,3 +1,57 @@
2017-06-08 Tamar Christina <tamar.christina@arm.com>
PR middle-end/77925
PR middle-end/77926
PR middle-end/66462
* gcc/builtins.c (fold_builtin_fpclassify): Remove.
(fold_builtin_interclass_mathfn): Remove.
(expand_builtin): Add builtins to lowering list.
(fold_builtin_n): Remove fold_builtin_varargs.
(fold_builtin_varargs): Remove.
* gcc/builtins.def (BUILT_IN_ISZERO, BUILT_IN_ISSUBNORMAL): New.
* gcc/real.h (get_min_float): New.
(real_format): Add is_ieee_compatible field.
* gcc/real.c (get_min_float): New.
(ieee_single_format): Set is_ieee_compatible flag.
* gcc/gimple-low.c (lower_stm): Define BUILT_IN_FPCLASSIFY,
CASE_FLT_FN (BUILT_IN_ISINF), BUILT_IN_ISINFD32, BUILT_IN_ISINFD64,
BUILT_IN_ISINFD128, BUILT_IN_ISNAND32, BUILT_IN_ISNAND64,
BUILT_IN_ISNAND128, BUILT_IN_ISNAN, BUILT_IN_ISNORMAL, BUILT_IN_ISZERO,
BUILT_IN_ISSUBNORMAL, CASE_FLT_FN (BUILT_IN_FINITE), BUILT_IN_FINITED32
BUILT_IN_FINITED64, BUILT_IN_FINITED128, BUILT_IN_ISFINITE.
(lower_builtin_fpclassify, is_nan, is_normal, is_infinity): New.
(is_zero, is_subnormal, is_finite, use_ieee_int_mode): Likewise.
(lower_builtin_isnan, lower_builtin_isinfinite): Likewise.
(lower_builtin_isnormal, lower_builtin_iszero): Likewise.
(lower_builtin_issubnormal, lower_builtin_isfinite): Likewise.
(emit_tree_cond, get_num_as_int, emit_tree_and_return_var): New.
(mips_single_format): Likewise.
(motorola_single_format): Likewise.
(spu_single_format): Likewise.
(ieee_double_format): Likewise.
(mips_double_format): Likewise.
(motorola_double_format): Likewise.
(ieee_extended_motorola_format): Likewise.
(ieee_extended_intel_128_format): Likewise.
(ieee_extended_intel_96_round_53_format): Likewise.
(ibm_extended_format): Likewise.
(mips_extended_format): Likewise.
(ieee_quad_format): Likewise.
(mips_quad_format): Likewise.
(vax_f_format): Likewise.
(vax_d_format): Likewise.
(vax_g_format): Likewise.
(decimal_single_format): Likewise.
(decimal_quad_format): Likewise.
(iee_half_format): Likewise.
(mips_single_format): Likewise.
(arm_half_format): Likewise.
(real_internal_format): Likewise.
* gcc/doc/extend.texi: Add documentation for built-ins.
* gcc/c/c-typeck.c (convert_arguments): Add BUILT_IN_ISZERO
and BUILT_IN_ISSUBNORMAL.
2017-06-07 Carl Love <cel@us.ibm.com>
* config/rs6000/rs6000-c: The return type of the following

View File

@ -165,7 +165,6 @@ static tree fold_builtin_0 (location_t, tree);
static tree fold_builtin_1 (location_t, tree, tree);
static tree fold_builtin_2 (location_t, tree, tree, tree);
static tree fold_builtin_3 (location_t, tree, tree, tree, tree);
static tree fold_builtin_varargs (location_t, tree, tree*, int);
static tree fold_builtin_strpbrk (location_t, tree, tree, tree);
static tree fold_builtin_strspn (location_t, tree, tree);
@ -2227,19 +2226,8 @@ interclass_mathfn_icode (tree arg, tree fndecl)
switch (DECL_FUNCTION_CODE (fndecl))
{
CASE_FLT_FN (BUILT_IN_ILOGB):
errno_set = true; builtin_optab = ilogb_optab; break;
CASE_FLT_FN (BUILT_IN_ISINF):
builtin_optab = isinf_optab; break;
case BUILT_IN_ISNORMAL:
case BUILT_IN_ISFINITE:
CASE_FLT_FN (BUILT_IN_FINITE):
case BUILT_IN_FINITED32:
case BUILT_IN_FINITED64:
case BUILT_IN_FINITED128:
case BUILT_IN_ISINFD32:
case BUILT_IN_ISINFD64:
case BUILT_IN_ISINFD128:
/* These builtins have no optabs (yet). */
errno_set = true;
builtin_optab = ilogb_optab;
break;
default:
gcc_unreachable ();
@ -2258,8 +2246,7 @@ interclass_mathfn_icode (tree arg, tree fndecl)
}
/* Expand a call to one of the builtin math functions that operate on
floating point argument and output an integer result (ilogb, isinf,
isnan, etc).
floating point argument and output an integer result (ilogb, etc).
Return 0 if a normal call should be emitted rather than expanding the
function in-line. EXP is the expression that is a call to the builtin
function; if convenient, the result should be placed in TARGET. */
@ -6605,11 +6592,7 @@ expand_builtin (tree exp, rtx target, rtx subtarget, machine_mode mode,
CASE_FLT_FN (BUILT_IN_ILOGB):
if (! flag_unsafe_math_optimizations)
break;
gcc_fallthrough ();
CASE_FLT_FN (BUILT_IN_ISINF):
CASE_FLT_FN (BUILT_IN_FINITE):
case BUILT_IN_ISFINITE:
case BUILT_IN_ISNORMAL:
target = expand_builtin_interclass_mathfn (exp, target);
if (target)
return target;
@ -6917,8 +6900,25 @@ expand_builtin (tree exp, rtx target, rtx subtarget, machine_mode mode,
}
break;
CASE_FLT_FN (BUILT_IN_ISINF):
case BUILT_IN_ISNAND32:
case BUILT_IN_ISNAND64:
case BUILT_IN_ISNAND128:
case BUILT_IN_ISNAN:
case BUILT_IN_ISINFD32:
case BUILT_IN_ISINFD64:
case BUILT_IN_ISINFD128:
case BUILT_IN_ISNORMAL:
case BUILT_IN_ISZERO:
case BUILT_IN_ISSUBNORMAL:
case BUILT_IN_FPCLASSIFY:
case BUILT_IN_SETJMP:
/* This should have been lowered to the builtins below. */
CASE_FLT_FN (BUILT_IN_FINITE):
case BUILT_IN_FINITED32:
case BUILT_IN_FINITED64:
case BUILT_IN_FINITED128:
case BUILT_IN_ISFINITE:
/* These should have been lowered to the builtins in gimple-low.c. */
gcc_unreachable ();
case BUILT_IN_SETJMP_SETUP:
@ -8258,184 +8258,19 @@ fold_builtin_modf (location_t loc, tree arg0, tree arg1, tree rettype)
return NULL_TREE;
}
/* Given a location LOC, an interclass builtin function decl FNDECL
and its single argument ARG, return an folded expression computing
the same, or NULL_TREE if we either couldn't or didn't want to fold
(the latter happen if there's an RTL instruction available). */
static tree
fold_builtin_interclass_mathfn (location_t loc, tree fndecl, tree arg)
{
machine_mode mode;
if (!validate_arg (arg, REAL_TYPE))
return NULL_TREE;
if (interclass_mathfn_icode (arg, fndecl) != CODE_FOR_nothing)
return NULL_TREE;
mode = TYPE_MODE (TREE_TYPE (arg));
bool is_ibm_extended = MODE_COMPOSITE_P (mode);
/* If there is no optab, try generic code. */
switch (DECL_FUNCTION_CODE (fndecl))
{
tree result;
CASE_FLT_FN (BUILT_IN_ISINF):
{
/* isinf(x) -> isgreater(fabs(x),DBL_MAX). */
tree const isgr_fn = builtin_decl_explicit (BUILT_IN_ISGREATER);
tree type = TREE_TYPE (arg);
REAL_VALUE_TYPE r;
char buf[128];
if (is_ibm_extended)
{
/* NaN and Inf are encoded in the high-order double value
only. The low-order value is not significant. */
type = double_type_node;
mode = DFmode;
arg = fold_build1_loc (loc, NOP_EXPR, type, arg);
}
get_max_float (REAL_MODE_FORMAT (mode), buf, sizeof (buf));
real_from_string (&r, buf);
result = build_call_expr (isgr_fn, 2,
fold_build1_loc (loc, ABS_EXPR, type, arg),
build_real (type, r));
return result;
}
CASE_FLT_FN (BUILT_IN_FINITE):
case BUILT_IN_ISFINITE:
{
/* isfinite(x) -> islessequal(fabs(x),DBL_MAX). */
tree const isle_fn = builtin_decl_explicit (BUILT_IN_ISLESSEQUAL);
tree type = TREE_TYPE (arg);
REAL_VALUE_TYPE r;
char buf[128];
if (is_ibm_extended)
{
/* NaN and Inf are encoded in the high-order double value
only. The low-order value is not significant. */
type = double_type_node;
mode = DFmode;
arg = fold_build1_loc (loc, NOP_EXPR, type, arg);
}
get_max_float (REAL_MODE_FORMAT (mode), buf, sizeof (buf));
real_from_string (&r, buf);
result = build_call_expr (isle_fn, 2,
fold_build1_loc (loc, ABS_EXPR, type, arg),
build_real (type, r));
/*result = fold_build2_loc (loc, UNGT_EXPR,
TREE_TYPE (TREE_TYPE (fndecl)),
fold_build1_loc (loc, ABS_EXPR, type, arg),
build_real (type, r));
result = fold_build1_loc (loc, TRUTH_NOT_EXPR,
TREE_TYPE (TREE_TYPE (fndecl)),
result);*/
return result;
}
case BUILT_IN_ISNORMAL:
{
/* isnormal(x) -> isgreaterequal(fabs(x),DBL_MIN) &
islessequal(fabs(x),DBL_MAX). */
tree const isle_fn = builtin_decl_explicit (BUILT_IN_ISLESSEQUAL);
tree type = TREE_TYPE (arg);
tree orig_arg, max_exp, min_exp;
machine_mode orig_mode = mode;
REAL_VALUE_TYPE rmax, rmin;
char buf[128];
orig_arg = arg = builtin_save_expr (arg);
if (is_ibm_extended)
{
/* Use double to test the normal range of IBM extended
precision. Emin for IBM extended precision is
different to emin for IEEE double, being 53 higher
since the low double exponent is at least 53 lower
than the high double exponent. */
type = double_type_node;
mode = DFmode;
arg = fold_build1_loc (loc, NOP_EXPR, type, arg);
}
arg = fold_build1_loc (loc, ABS_EXPR, type, arg);
get_max_float (REAL_MODE_FORMAT (mode), buf, sizeof (buf));
real_from_string (&rmax, buf);
sprintf (buf, "0x1p%d", REAL_MODE_FORMAT (orig_mode)->emin - 1);
real_from_string (&rmin, buf);
max_exp = build_real (type, rmax);
min_exp = build_real (type, rmin);
max_exp = build_call_expr (isle_fn, 2, arg, max_exp);
if (is_ibm_extended)
{
/* Testing the high end of the range is done just using
the high double, using the same test as isfinite().
For the subnormal end of the range we first test the
high double, then if its magnitude is equal to the
limit of 0x1p-969, we test whether the low double is
non-zero and opposite sign to the high double. */
tree const islt_fn = builtin_decl_explicit (BUILT_IN_ISLESS);
tree const isgt_fn = builtin_decl_explicit (BUILT_IN_ISGREATER);
tree gt_min = build_call_expr (isgt_fn, 2, arg, min_exp);
tree eq_min = fold_build2 (EQ_EXPR, integer_type_node,
arg, min_exp);
tree as_complex = build1 (VIEW_CONVERT_EXPR,
complex_double_type_node, orig_arg);
tree hi_dbl = build1 (REALPART_EXPR, type, as_complex);
tree lo_dbl = build1 (IMAGPART_EXPR, type, as_complex);
tree zero = build_real (type, dconst0);
tree hilt = build_call_expr (islt_fn, 2, hi_dbl, zero);
tree lolt = build_call_expr (islt_fn, 2, lo_dbl, zero);
tree logt = build_call_expr (isgt_fn, 2, lo_dbl, zero);
tree ok_lo = fold_build1 (TRUTH_NOT_EXPR, integer_type_node,
fold_build3 (COND_EXPR,
integer_type_node,
hilt, logt, lolt));
eq_min = fold_build2 (TRUTH_ANDIF_EXPR, integer_type_node,
eq_min, ok_lo);
min_exp = fold_build2 (TRUTH_ORIF_EXPR, integer_type_node,
gt_min, eq_min);
}
else
{
tree const isge_fn
= builtin_decl_explicit (BUILT_IN_ISGREATEREQUAL);
min_exp = build_call_expr (isge_fn, 2, arg, min_exp);
}
result = fold_build2 (BIT_AND_EXPR, integer_type_node,
max_exp, min_exp);
return result;
}
default:
break;
}
return NULL_TREE;
}
/* Fold a call to __builtin_isnan(), __builtin_isinf, __builtin_finite.
/* Fold a call to __builtin_isinf_sign.
ARG is the argument for the call. */
static tree
fold_builtin_classify (location_t loc, tree fndecl, tree arg, int builtin_index)
fold_builtin_classify (location_t loc, tree arg, int builtin_index)
{
tree type = TREE_TYPE (TREE_TYPE (fndecl));
if (!validate_arg (arg, REAL_TYPE))
return NULL_TREE;
switch (builtin_index)
{
case BUILT_IN_ISINF:
if (!HONOR_INFINITIES (arg))
return omit_one_operand_loc (loc, type, integer_zero_node, arg);
return NULL_TREE;
case BUILT_IN_ISINF_SIGN:
{
/* isinf_sign(x) -> isinf(x) ? (signbit(x) ? -1 : 1) : 0 */
@ -8468,106 +8303,11 @@ fold_builtin_classify (location_t loc, tree fndecl, tree arg, int builtin_index)
return tmp;
}
case BUILT_IN_ISFINITE:
if (!HONOR_NANS (arg)
&& !HONOR_INFINITIES (arg))
return omit_one_operand_loc (loc, type, integer_one_node, arg);
return NULL_TREE;
case BUILT_IN_ISNAN:
if (!HONOR_NANS (arg))
return omit_one_operand_loc (loc, type, integer_zero_node, arg);
{
bool is_ibm_extended = MODE_COMPOSITE_P (TYPE_MODE (TREE_TYPE (arg)));
if (is_ibm_extended)
{
/* NaN and Inf are encoded in the high-order double value
only. The low-order value is not significant. */
arg = fold_build1_loc (loc, NOP_EXPR, double_type_node, arg);
}
}
arg = builtin_save_expr (arg);
return fold_build2_loc (loc, UNORDERED_EXPR, type, arg, arg);
default:
gcc_unreachable ();
}
}
/* Fold a call to __builtin_fpclassify(int, int, int, int, int, ...).
This builtin will generate code to return the appropriate floating
point classification depending on the value of the floating point
number passed in. The possible return values must be supplied as
int arguments to the call in the following order: FP_NAN, FP_INFINITE,
FP_NORMAL, FP_SUBNORMAL and FP_ZERO. The ellipses is for exactly
one floating point argument which is "type generic". */
static tree
fold_builtin_fpclassify (location_t loc, tree *args, int nargs)
{
tree fp_nan, fp_infinite, fp_normal, fp_subnormal, fp_zero,
arg, type, res, tmp;
machine_mode mode;
REAL_VALUE_TYPE r;
char buf[128];
/* Verify the required arguments in the original call. */
if (nargs != 6
|| !validate_arg (args[0], INTEGER_TYPE)
|| !validate_arg (args[1], INTEGER_TYPE)
|| !validate_arg (args[2], INTEGER_TYPE)
|| !validate_arg (args[3], INTEGER_TYPE)
|| !validate_arg (args[4], INTEGER_TYPE)
|| !validate_arg (args[5], REAL_TYPE))
return NULL_TREE;
fp_nan = args[0];
fp_infinite = args[1];
fp_normal = args[2];
fp_subnormal = args[3];
fp_zero = args[4];
arg = args[5];
type = TREE_TYPE (arg);
mode = TYPE_MODE (type);
arg = builtin_save_expr (fold_build1_loc (loc, ABS_EXPR, type, arg));
/* fpclassify(x) ->
isnan(x) ? FP_NAN :
(fabs(x) == Inf ? FP_INFINITE :
(fabs(x) >= DBL_MIN ? FP_NORMAL :
(x == 0 ? FP_ZERO : FP_SUBNORMAL))). */
tmp = fold_build2_loc (loc, EQ_EXPR, integer_type_node, arg,
build_real (type, dconst0));
res = fold_build3_loc (loc, COND_EXPR, integer_type_node,
tmp, fp_zero, fp_subnormal);
sprintf (buf, "0x1p%d", REAL_MODE_FORMAT (mode)->emin - 1);
real_from_string (&r, buf);
tmp = fold_build2_loc (loc, GE_EXPR, integer_type_node,
arg, build_real (type, r));
res = fold_build3_loc (loc, COND_EXPR, integer_type_node, tmp, fp_normal, res);
if (HONOR_INFINITIES (mode))
{
real_inf (&r);
tmp = fold_build2_loc (loc, EQ_EXPR, integer_type_node, arg,
build_real (type, r));
res = fold_build3_loc (loc, COND_EXPR, integer_type_node, tmp,
fp_infinite, res);
}
if (HONOR_NANS (mode))
{
tmp = fold_build2_loc (loc, ORDERED_EXPR, integer_type_node, arg, arg);
res = fold_build3_loc (loc, COND_EXPR, integer_type_node, tmp, res, fp_nan);
}
return res;
}
/* Fold a call to an unordered comparison function such as
__builtin_isgreater(). FNDECL is the FUNCTION_DECL for the function
being called and ARG0 and ARG1 are the arguments for the call.
@ -8868,40 +8608,8 @@ fold_builtin_1 (location_t loc, tree fndecl, tree arg0)
case BUILT_IN_ISDIGIT:
return fold_builtin_isdigit (loc, arg0);
CASE_FLT_FN (BUILT_IN_FINITE):
case BUILT_IN_FINITED32:
case BUILT_IN_FINITED64:
case BUILT_IN_FINITED128:
case BUILT_IN_ISFINITE:
{
tree ret = fold_builtin_classify (loc, fndecl, arg0, BUILT_IN_ISFINITE);
if (ret)
return ret;
return fold_builtin_interclass_mathfn (loc, fndecl, arg0);
}
CASE_FLT_FN (BUILT_IN_ISINF):
case BUILT_IN_ISINFD32:
case BUILT_IN_ISINFD64:
case BUILT_IN_ISINFD128:
{
tree ret = fold_builtin_classify (loc, fndecl, arg0, BUILT_IN_ISINF);
if (ret)
return ret;
return fold_builtin_interclass_mathfn (loc, fndecl, arg0);
}
case BUILT_IN_ISNORMAL:
return fold_builtin_interclass_mathfn (loc, fndecl, arg0);
case BUILT_IN_ISINF_SIGN:
return fold_builtin_classify (loc, fndecl, arg0, BUILT_IN_ISINF_SIGN);
CASE_FLT_FN (BUILT_IN_ISNAN):
case BUILT_IN_ISNAND32:
case BUILT_IN_ISNAND64:
case BUILT_IN_ISNAND128:
return fold_builtin_classify (loc, fndecl, arg0, BUILT_IN_ISNAN);
return fold_builtin_classify (loc, arg0, BUILT_IN_ISINF_SIGN);
case BUILT_IN_FREE:
if (integer_zerop (arg0))
@ -9098,7 +8806,6 @@ fold_builtin_n (location_t loc, tree fndecl, tree *args, int nargs, bool)
ret = fold_builtin_3 (loc, fndecl, args[0], args[1], args[2]);
break;
default:
ret = fold_builtin_varargs (loc, fndecl, args, nargs);
break;
}
if (ret)
@ -9989,37 +9696,6 @@ fold_builtin_object_size (tree ptr, tree ost)
return NULL_TREE;
}
/* Builtins with folding operations that operate on "..." arguments
need special handling; we need to store the arguments in a convenient
data structure before attempting any folding. Fortunately there are
only a few builtins that fall into this category. FNDECL is the
function, EXP is the CALL_EXPR for the call. */
static tree
fold_builtin_varargs (location_t loc, tree fndecl, tree *args, int nargs)
{
enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
tree ret = NULL_TREE;
switch (fcode)
{
case BUILT_IN_FPCLASSIFY:
ret = fold_builtin_fpclassify (loc, args, nargs);
break;
default:
break;
}
if (ret)
{
ret = build1 (NOP_EXPR, TREE_TYPE (ret), ret);
SET_EXPR_LOCATION (ret, loc);
TREE_NO_WARNING (ret) = 1;
return ret;
}
return NULL_TREE;
}
/* Initialize format string characters in the target charset. */
bool

View File

@ -845,6 +845,8 @@ DEF_EXT_LIB_BUILTIN (BUILT_IN_ISINFL, "isinfl", BT_FN_INT_LONGDOUBLE, ATTR_CO
DEF_EXT_LIB_BUILTIN (BUILT_IN_ISINFD32, "isinfd32", BT_FN_INT_DFLOAT32, ATTR_CONST_NOTHROW_LEAF_LIST)
DEF_EXT_LIB_BUILTIN (BUILT_IN_ISINFD64, "isinfd64", BT_FN_INT_DFLOAT64, ATTR_CONST_NOTHROW_LEAF_LIST)
DEF_EXT_LIB_BUILTIN (BUILT_IN_ISINFD128, "isinfd128", BT_FN_INT_DFLOAT128, ATTR_CONST_NOTHROW_LEAF_LIST)
DEF_GCC_BUILTIN (BUILT_IN_ISZERO, "iszero", BT_FN_INT_VAR, ATTR_CONST_NOTHROW_TYPEGENERIC_LEAF)
DEF_GCC_BUILTIN (BUILT_IN_ISSUBNORMAL, "issubnormal", BT_FN_INT_VAR, ATTR_CONST_NOTHROW_TYPEGENERIC_LEAF)
DEF_C99_C90RES_BUILTIN (BUILT_IN_ISNAN, "isnan", BT_FN_INT_VAR, ATTR_CONST_NOTHROW_TYPEGENERIC_LEAF)
DEF_EXT_LIB_BUILTIN (BUILT_IN_ISNANF, "isnanf", BT_FN_INT_FLOAT, ATTR_CONST_NOTHROW_LEAF_LIST)
DEF_EXT_LIB_BUILTIN (BUILT_IN_ISNANL, "isnanl", BT_FN_INT_LONGDOUBLE, ATTR_CONST_NOTHROW_LEAF_LIST)

View File

@ -3241,6 +3241,8 @@ convert_arguments (location_t loc, vec<location_t> arg_loc, tree typelist,
case BUILT_IN_ISINF_SIGN:
case BUILT_IN_ISNAN:
case BUILT_IN_ISNORMAL:
case BUILT_IN_ISZERO:
case BUILT_IN_ISSUBNORMAL:
case BUILT_IN_FPCLASSIFY:
type_generic_remove_excess_precision = true;
break;

View File

@ -10501,6 +10501,10 @@ in the Cilk Plus language manual which can be found at
@findex __builtin_isgreater
@findex __builtin_isgreaterequal
@findex __builtin_isinf_sign
@findex __builtin_isinf
@findex __builtin_isnan
@findex __builtin_iszero
@findex __builtin_issubnormal
@findex __builtin_isless
@findex __builtin_islessequal
@findex __builtin_islessgreater
@ -11564,7 +11568,54 @@ constant values and they must appear in this order: @code{FP_NAN},
@code{FP_INFINITE}, @code{FP_NORMAL}, @code{FP_SUBNORMAL} and
@code{FP_ZERO}. The ellipsis is for exactly one floating-point value
to classify. GCC treats the last argument as type-generic, which
means it does not do default promotion from float to double.
means it does not do default promotion from @code{float} to @code{double}.
@end deftypefn
@deftypefn {Built-in Function} int __builtin_isnan (...)
This built-in implements the C99 isnan functionality which checks if
the given argument represents a NaN. The return value of the
function will either be a 0 (false) or a 1 (true).
On most systems, when an IEEE 754 floating-point type is used this
built-in does not produce a signal when a signaling NaN is used.
GCC treats the argument as type-generic, which means it does
not do default promotion from @code{float} to @code{double}.
@end deftypefn
@deftypefn {Built-in Function} int __builtin_isinf (...)
This built-in implements the C99 isinf functionality which checks if
the given argument represents an infinite number. The return
value of the function will either be a 0 (false) or a 1 (true).
GCC treats the argument as type-generic, which means it does
not do default promotion from @code{float} to @code{double}.
@end deftypefn
@deftypefn {Built-in Function} int __builtin_isnormal (...)
This built-in implements the C99 isnormal functionality which checks if
the given argument represents a normal number. The return
value of the function will either be a 0 (false) or a 1 (true).
GCC treats the argument as type-generic, which means it does
not do default promotion from @code{float} to @code{double}.
@end deftypefn
@deftypefn {Built-in Function} int __builtin_iszero (...)
This built-in implements the TS 18661-1:2014 iszero functionality which checks if
the given argument represents the number 0 or -0. The return
value of the function will either be a 0 (false) or a 1 (true).
GCC treats the argument as type-generic, which means it does
not do default promotion from @code{float} to @code{double}.
@end deftypefn
@deftypefn {Built-in Function} int __builtin_issubnormal (...)
This built-in implements the TS 18661-1:2014 issubnormal functionality which checks if
the given argument represents a subnormal number. The return
value of the function will either be a 0 (false) or a 1 (true).
GCC treats the argument as type-generic, which means it does
not do default promotion from @code{float} to @code{double}.
@end deftypefn
@deftypefn {Built-in Function} double __builtin_inf (void)

View File

@ -30,6 +30,9 @@ along with GCC; see the file COPYING3. If not see
#include "calls.h"
#include "gimple-iterator.h"
#include "gimple-low.h"
#include "stor-layout.h"
#include "target.h"
#include "gimplify.h"
/* The differences between High GIMPLE and Low GIMPLE are the
following:
@ -72,6 +75,13 @@ static void lower_gimple_bind (gimple_stmt_iterator *, struct lower_data *);
static void lower_try_catch (gimple_stmt_iterator *, struct lower_data *);
static void lower_gimple_return (gimple_stmt_iterator *, struct lower_data *);
static void lower_builtin_setjmp (gimple_stmt_iterator *);
static void lower_builtin_fpclassify (gimple_stmt_iterator *);
static void lower_builtin_isnan (gimple_stmt_iterator *);
static void lower_builtin_isinfinite (gimple_stmt_iterator *);
static void lower_builtin_isnormal (gimple_stmt_iterator *);
static void lower_builtin_iszero (gimple_stmt_iterator *);
static void lower_builtin_issubnormal (gimple_stmt_iterator *);
static void lower_builtin_isfinite (gimple_stmt_iterator *);
static void lower_builtin_posix_memalign (gimple_stmt_iterator *);
@ -330,18 +340,69 @@ lower_stmt (gimple_stmt_iterator *gsi, struct lower_data *data)
if (decl
&& DECL_BUILT_IN_CLASS (decl) == BUILT_IN_NORMAL)
{
if (DECL_FUNCTION_CODE (decl) == BUILT_IN_SETJMP)
switch (DECL_FUNCTION_CODE (decl))
{
case BUILT_IN_SETJMP:
lower_builtin_setjmp (gsi);
data->cannot_fallthru = false;
return;
}
else if (DECL_FUNCTION_CODE (decl) == BUILT_IN_POSIX_MEMALIGN
&& flag_tree_bit_ccp
&& gimple_builtin_call_types_compatible_p (stmt, decl))
{
lower_builtin_posix_memalign (gsi);
case BUILT_IN_POSIX_MEMALIGN:
if (flag_tree_bit_ccp
&& gimple_builtin_call_types_compatible_p (stmt, decl))
{
lower_builtin_posix_memalign (gsi);
return;
}
break;
case BUILT_IN_FPCLASSIFY:
lower_builtin_fpclassify (gsi);
data->cannot_fallthru = false;
return;
CASE_FLT_FN (BUILT_IN_ISINF):
case BUILT_IN_ISINFD32:
case BUILT_IN_ISINFD64:
case BUILT_IN_ISINFD128:
lower_builtin_isinfinite (gsi);
data->cannot_fallthru = false;
return;
case BUILT_IN_ISNAND32:
case BUILT_IN_ISNAND64:
case BUILT_IN_ISNAND128:
CASE_FLT_FN (BUILT_IN_ISNAN):
lower_builtin_isnan (gsi);
data->cannot_fallthru = false;
return;
case BUILT_IN_ISNORMAL:
lower_builtin_isnormal (gsi);
data->cannot_fallthru = false;
return;
case BUILT_IN_ISZERO:
lower_builtin_iszero (gsi);
data->cannot_fallthru = false;
return;
case BUILT_IN_ISSUBNORMAL:
lower_builtin_issubnormal (gsi);
data->cannot_fallthru = false;
return;
CASE_FLT_FN (BUILT_IN_FINITE):
case BUILT_IN_FINITED32:
case BUILT_IN_FINITED64:
case BUILT_IN_FINITED128:
case BUILT_IN_ISFINITE:
lower_builtin_isfinite (gsi);
data->cannot_fallthru = false;
return;
default:
break;
}
}
@ -822,6 +883,841 @@ lower_builtin_setjmp (gimple_stmt_iterator *gsi)
gsi_remove (gsi, false);
}
/* This function will if ARG is not already a variable or SSA_NAME,
create a new temporary TMP and bind ARG to TMP. This new binding is then
emitted into SEQ and TMP is returned. */
static tree
emit_tree_and_return_var (gimple_seq *seq, tree arg)
{
if (TREE_CODE (arg) == SSA_NAME || VAR_P (arg))
return arg;
tree tmp = create_tmp_reg (TREE_TYPE (arg));
gassign *stm = gimple_build_assign (tmp, arg);
gimple_seq_add_stmt (seq, stm);
return tmp;
}
/* This function builds an if statement that ends up using explicit branches
instead of becoming a ternary conditional select. This function assumes you
will fall through to the next statements after the condition for the false
branch. The code emitted looks like:
if (COND)
RESULT_VARIABLE = TRUE_BRANCH
GOTO EXIT_LABEL
else
...
SEQ is the gimple sequence/buffer to emit any new bindings to.
RESULT_VARIABLE is the value to set if COND.
EXIT_LABEL is the label to jump to in case COND.
COND is condition to use in the conditional statement of the if.
TRUE_BRANCH is the value to set RESULT_VARIABLE to if COND. */
static void
emit_tree_cond (gimple_seq *seq, tree result_variable, tree exit_label,
tree cond, tree true_branch)
{
/* Create labels for fall through. */
tree true_label = create_artificial_label (UNKNOWN_LOCATION);
tree false_label = create_artificial_label (UNKNOWN_LOCATION);
gcond *stmt = gimple_build_cond_from_tree (cond, true_label, false_label);
gimple_seq_add_stmt (seq, stmt);
/* Build the true case. */
gimple_seq_add_stmt (seq, gimple_build_label (true_label));
tree value = TREE_CONSTANT (true_branch)
? true_branch
: emit_tree_and_return_var (seq, true_branch);
gimple_seq_add_stmt (seq, gimple_build_assign (result_variable, value));
gimple_seq_add_stmt (seq, gimple_build_goto (exit_label));
/* Build the false case. */
gimple_seq_add_stmt (seq, gimple_build_label (false_label));
}
/* This function returns a variable containing an reinterpreted ARG as an
integer.
SEQ is the gimple sequence/buffer to write any new bindings to.
ARG is the floating point number to reinterpret as an integer.
LOC is the location to use when doing folding operations. */
static tree
get_num_as_int (gimple_seq *seq, tree arg, location_t loc)
{
tree type = TREE_TYPE (arg);
const HOST_WIDE_INT type_width = TYPE_PRECISION (type);
/* Re-interpret the float as an unsigned integer type
with equal precision. */
tree int_arg_type = build_nonstandard_integer_type (type_width, true);
tree conv_arg = fold_build1_loc (loc, VIEW_CONVERT_EXPR, int_arg_type, arg);
return emit_tree_and_return_var (seq, conv_arg);
}
/* Check if ARG which is the floating point number being classified is close
enough to IEEE 754 format to be able to go in the early exit code. */
static bool
use_ieee_int_mode (tree arg)
{
tree type = TREE_TYPE (arg);
machine_mode mode = TYPE_MODE (type);
const real_format *format = REAL_MODE_FORMAT (mode);
machine_mode imode = int_mode_for_mode (mode);
bool is_ibm_extended = MODE_COMPOSITE_P (mode);
return (format->is_binary_ieee_compatible
&& FLOAT_WORDS_BIG_ENDIAN == WORDS_BIG_ENDIAN
/* Check if there's a usable integer mode. */
&& imode != BLKmode
&& targetm.scalar_mode_supported_p (imode)
&& !is_ibm_extended);
}
/* Perform some IBM extended format fixups on ARG for use by FP functions.
This is done by ignoring the lower 64 bits of the number.
MODE is the machine mode of ARG.
TYPE is the type of ARG.
LOC is the location to be used in fold functions. Usually is the location
of the definition of ARG. */
static bool
perform_ibm_extended_fixups (tree *arg, machine_mode *mode,
tree *type, location_t loc)
{
bool is_ibm_extended = MODE_COMPOSITE_P (*mode);
if (is_ibm_extended)
{
/* NaN and Inf are encoded in the high-order double value
only. The low-order value is not significant. */
*type = double_type_node;
*mode = DFmode;
*arg = fold_build1_loc (loc, NOP_EXPR, *type, *arg);
}
return is_ibm_extended;
}
/* Generates code to check if ARG is a normal number. For the FP case we check
MIN_VALUE(ARG) <= ABS(ARG) > INF and for the INT value we check the exp and
mantissa bits. Returns a variable containing a boolean which has the result
of the check.
SEQ is the buffer to use to emit the gimple instructions into.
LOC is the location to use during fold calls. */
static tree
is_normal (gimple_seq *seq, tree arg, location_t loc)
{
tree type = TREE_TYPE (arg);
machine_mode mode = TYPE_MODE (type);
const real_format *format = REAL_MODE_FORMAT (mode);
const tree bool_type = boolean_type_node;
/* If not using optimized route then exit early. */
if (!use_ieee_int_mode (arg))
{
tree orig_arg = arg;
machine_mode orig_mode = mode;
if (TREE_CODE (arg) != SSA_NAME
&& (TREE_ADDRESSABLE (arg) != 0
|| (TREE_CODE (arg) != PARM_DECL
&& (!VAR_P (arg) || TREE_STATIC (arg)))))
orig_arg = save_expr (arg);
/* Perform IBM extended format fixups if required. */
bool is_ibm_extended = perform_ibm_extended_fixups (&arg, &mode,
&type, loc);
REAL_VALUE_TYPE rinf, rmin;
tree arg_p = fold_build1_loc (loc, ABS_EXPR, type, arg);
tree const islt_fn = builtin_decl_explicit (BUILT_IN_ISLESS);
tree const isgt_fn = builtin_decl_explicit (BUILT_IN_ISGREATER);
tree const isge_fn = builtin_decl_explicit (BUILT_IN_ISGREATEREQUAL);
char buf[128];
real_inf (&rinf);
get_min_float (REAL_MODE_FORMAT (orig_mode), buf, sizeof (buf));
real_from_string (&rmin, buf);
tree inf_exp = build_call_expr (islt_fn, 2, arg_p,
build_real (type, rinf));
tree min_exp = build_real (type, rmin);
if (is_ibm_extended)
{
/* Testing the high end of the range is done just using
the high double, using the same test as isfinite().
For the subnormal end of the range we first test the
high double, then if its magnitude is equal to the
limit of 0x1p-969, we test whether the low double is
non-zero and opposite sign to the high double. */
tree gt_min = build_call_expr (isgt_fn, 2, arg_p, min_exp);
tree eq_min = fold_build2 (EQ_EXPR, integer_type_node,
arg_p, min_exp);
tree as_complex = build1 (VIEW_CONVERT_EXPR,
complex_double_type_node, orig_arg);
tree hi_dbl = build1 (REALPART_EXPR, type, as_complex);
tree lo_dbl = build1 (IMAGPART_EXPR, type, as_complex);
tree zero = build_real (type, dconst0);
tree hilt = build_call_expr (islt_fn, 2, hi_dbl, zero);
tree lolt = build_call_expr (islt_fn, 2, lo_dbl, zero);
tree logt = build_call_expr (isgt_fn, 2, lo_dbl, zero);
tree ok_lo = fold_build1 (TRUTH_NOT_EXPR, integer_type_node,
fold_build3 (COND_EXPR,
integer_type_node,
hilt, logt, lolt));
eq_min = fold_build2 (TRUTH_ANDIF_EXPR, integer_type_node,
eq_min, ok_lo);
min_exp = fold_build2 (TRUTH_ORIF_EXPR, integer_type_node,
gt_min, eq_min);
}
else
{
min_exp = build_call_expr (isge_fn, 2, arg_p, min_exp);
}
push_gimplify_context ();
gimplify_expr (&min_exp, seq, NULL, is_gimple_val, fb_either);
gimplify_expr (&inf_exp, seq, NULL, is_gimple_val, fb_either);
tree res
= fold_build2_loc (loc, BIT_AND_EXPR, bool_type,
emit_tree_and_return_var (seq,
gimple_boolify (min_exp)),
emit_tree_and_return_var (seq,
gimple_boolify (inf_exp)));
pop_gimplify_context (NULL);
return emit_tree_and_return_var (seq, res);
}
const tree int_type = unsigned_type_node;
const int exp_bits = (GET_MODE_SIZE (mode) * BITS_PER_UNIT) - format->p;
const int exp_mask = (1 << exp_bits) - 1;
/* Get the number reinterpreted as an integer. */
tree int_arg = get_num_as_int (seq, arg, loc);
/* Extract exp bits from the float, where we expect the exponent to be.
We create a new type because BIT_FIELD_REF does not allow you to
extract less bits than the precision of the storage variable. */
tree exp_tmp
= fold_build3_loc (loc, BIT_FIELD_REF,
build_nonstandard_integer_type (exp_bits, true),
int_arg,
build_int_cstu (int_type, exp_bits),
build_int_cstu (int_type, format->p - 1));
tree exp_bitfield = emit_tree_and_return_var (seq, exp_tmp);
/* Re-interpret the extracted exponent bits as a 32 bit int.
This allows us to continue doing operations as int_type. */
tree exp
= emit_tree_and_return_var (seq, fold_build1_loc (loc, NOP_EXPR, int_type,
exp_bitfield));
/* exp_mask & ~1. */
tree mask_check
= fold_build2_loc (loc, BIT_AND_EXPR, int_type,
build_int_cstu (int_type, exp_mask),
fold_build1_loc (loc, BIT_NOT_EXPR, int_type,
build_int_cstu (int_type, 1)));
/* (exp + 1) & mask_check.
Check to see if exp is not all 0 or all 1. */
tree exp_check
= fold_build2_loc (loc, BIT_AND_EXPR, int_type,
emit_tree_and_return_var (seq,
fold_build2_loc (loc, PLUS_EXPR, int_type, exp,
build_int_cstu (int_type, 1))),
mask_check);
tree res = fold_build2_loc (loc, NE_EXPR, boolean_type_node,
build_int_cstu (int_type, 0),
emit_tree_and_return_var (seq, exp_check));
return emit_tree_and_return_var (seq, res);
}
/* Generates code to check if ARG is a zero. For both the FP and INT case we
check if ARG == 0 (modulo sign bit). Returns a variable containing a boolean
which has the result of the check.
SEQ is the buffer to use to emit the gimple instructions into.
LOC is the location to use during fold calls. */
static tree
is_zero (gimple_seq *seq, tree arg, location_t loc)
{
tree type = TREE_TYPE (arg);
/* If not using optimized route then exit early. */
if (!use_ieee_int_mode (arg))
{
machine_mode mode = TYPE_MODE (type);
/* Perform IBM extended format fixups if required. */
perform_ibm_extended_fixups (&arg, &mode, &type, loc);
tree res = fold_build2_loc (loc, EQ_EXPR, boolean_type_node, arg,
build_real (type, dconst0));
return emit_tree_and_return_var (seq, res);
}
const HOST_WIDE_INT type_width = TYPE_PRECISION (type);
tree int_arg_type = build_nonstandard_integer_type (type_width, true);
/* Get the number reinterpreted as an integer.
Shift left to remove the sign. */
tree int_arg
= fold_build2_loc (loc, LSHIFT_EXPR, int_arg_type,
get_num_as_int (seq, arg, loc),
build_int_cstu (int_arg_type, 1));
/* num << 1 == 0.
This checks to see if the number is zero. */
tree zero_check
= fold_build2_loc (loc, EQ_EXPR, boolean_type_node,
build_int_cstu (int_arg_type, 0),
emit_tree_and_return_var (seq, int_arg));
return emit_tree_and_return_var (seq, zero_check);
}
/* Generates code to check if ARG is a subnormal number. In the FP case we test
fabs (ARG) != 0 && fabs (ARG) < MIN_VALUE (ARG) and in the INT case we check
the exp and mantissa bits on ARG. Returns a variable containing a boolean
which has the result of the check.
SEQ is the buffer to use to emit the gimple instructions into.
LOC is the location to use during fold calls. */
static tree
is_subnormal (gimple_seq *seq, tree arg, location_t loc)
{
const tree bool_type = boolean_type_node;
tree type = TREE_TYPE (arg);
machine_mode mode = TYPE_MODE (type);
const real_format *format = REAL_MODE_FORMAT (mode);
const HOST_WIDE_INT type_width = TYPE_PRECISION (type);
tree int_arg_type = build_nonstandard_integer_type (type_width, true);
/* If not using optimized route then exit early. */
if (!use_ieee_int_mode (arg))
{
tree const islt_fn = builtin_decl_explicit (BUILT_IN_ISLESS);
tree const isgt_fn = builtin_decl_explicit (BUILT_IN_ISGREATER);
tree arg_p
= emit_tree_and_return_var (seq, fold_build1_loc (loc, ABS_EXPR, type,
arg));
REAL_VALUE_TYPE r;
char buf[128];
get_min_float (REAL_MODE_FORMAT (mode), buf, sizeof (buf));
real_from_string (&r, buf);
tree subnorm = build_call_expr (islt_fn, 2, arg_p, build_real (type, r));
tree zero = build_call_expr (isgt_fn, 2, arg_p,
build_real (type, dconst0));
push_gimplify_context ();
gimplify_expr (&subnorm, seq, NULL, is_gimple_val, fb_either);
gimplify_expr (&zero, seq, NULL, is_gimple_val, fb_either);
tree res
= fold_build2_loc (loc, BIT_AND_EXPR, bool_type,
emit_tree_and_return_var (seq,
gimple_boolify (subnorm)),
emit_tree_and_return_var (seq,
gimple_boolify (zero)));
pop_gimplify_context (NULL);
return emit_tree_and_return_var (seq, res);
}
/* Get the number reinterpreted as an integer.
Shift left to remove the sign. */
tree int_arg
= fold_build2_loc (loc, LSHIFT_EXPR, int_arg_type,
get_num_as_int (seq, arg, loc),
build_int_cstu (int_arg_type, 1));
/* Check for a zero exponent and non-zero mantissa.
This can be done with two comparisons by first apply a
removing the sign bit and checking if the value is larger
than the mantissa mask. */
/* This creates a mask to be used to check the mantissa value in the shifted
integer representation of the fpnum. */
tree significant_bit = build_int_cstu (int_arg_type, format->p - 1);
tree mantissa_mask
= fold_build2_loc (loc, MINUS_EXPR, int_arg_type,
fold_build2_loc (loc, LSHIFT_EXPR, int_arg_type,
build_int_cstu (int_arg_type, 2),
significant_bit),
build_int_cstu (int_arg_type, 1));
/* Check if exponent is zero and mantissa is not. */
tree subnorm_cond_tmp
= fold_build2_loc (loc, LE_EXPR, bool_type,
emit_tree_and_return_var (seq, int_arg),
mantissa_mask);
tree subnorm_cond = emit_tree_and_return_var (seq, subnorm_cond_tmp);
tree zero_cond
= fold_build2_loc (loc, GT_EXPR, boolean_type_node,
emit_tree_and_return_var (seq, int_arg),
build_int_cstu (int_arg_type, 0));
tree subnorm_check
= fold_build2_loc (loc, BIT_AND_EXPR, boolean_type_node,
emit_tree_and_return_var (seq, subnorm_cond),
emit_tree_and_return_var (seq, zero_cond));
return emit_tree_and_return_var (seq, subnorm_check);
}
/* Generates code to check if ARG is an infinity. In the FP case we test
FABS(ARG) == INF and in the INT case we check the bits on the exp and
mantissa. Returns a variable containing a boolean which has the result
of the check.
SEQ is the buffer to use to emit the gimple instructions into.
LOC is the location to use during fold calls. */
static tree
is_infinity (gimple_seq *seq, tree arg, location_t loc)
{
tree type = TREE_TYPE (arg);
machine_mode mode = TYPE_MODE (type);
const tree bool_type = boolean_type_node;
if (!HONOR_INFINITIES (mode))
{
return build_int_cst (bool_type, false);
}
/* If not using optimized route then exit early. */
if (!use_ieee_int_mode (arg))
{
/* Perform IBM extended format fixups if required. */
perform_ibm_extended_fixups (&arg, &mode, &type, loc);
tree arg_p
= emit_tree_and_return_var (seq, fold_build1_loc (loc, ABS_EXPR, type,
arg));
REAL_VALUE_TYPE r;
real_inf (&r);
tree res = fold_build2_loc (loc, EQ_EXPR, bool_type, arg_p,
build_real (type, r));
return emit_tree_and_return_var (seq, res);
}
const real_format *format = REAL_MODE_FORMAT (mode);
const HOST_WIDE_INT type_width = TYPE_PRECISION (type);
tree int_arg_type = build_nonstandard_integer_type (type_width, true);
/* This creates a mask to be used to check the exp value in the shifted
integer representation of the fpnum. */
const int exp_bits = (GET_MODE_SIZE (mode) * BITS_PER_UNIT) - format->p;
gcc_assert (format->p > 0);
tree significant_bit = build_int_cstu (int_arg_type, format->p);
tree exp_mask
= fold_build2_loc (loc, MINUS_EXPR, int_arg_type,
fold_build2_loc (loc, LSHIFT_EXPR, int_arg_type,
build_int_cstu (int_arg_type, 2),
build_int_cstu (int_arg_type,
exp_bits - 1)),
build_int_cstu (int_arg_type, 1));
/* Get the number reinterpreted as an integer.
Shift left to remove the sign. */
tree int_arg
= fold_build2_loc (loc, LSHIFT_EXPR, int_arg_type,
get_num_as_int (seq, arg, loc),
build_int_cstu (int_arg_type, 1));
/* This mask checks to see if the exp has all bits set and mantissa no
bits set. */
tree inf_mask
= fold_build2_loc (loc, LSHIFT_EXPR, int_arg_type,
exp_mask, significant_bit);
/* Check if exponent has all bits set and mantissa is 0. */
tree inf_check
= emit_tree_and_return_var(seq,
fold_build2_loc (loc, EQ_EXPR, bool_type,
emit_tree_and_return_var(seq, int_arg),
inf_mask));
return emit_tree_and_return_var (seq, inf_check);
}
/* Generates code to check if ARG is a finite number. In the FP case we check
if FABS(ARG) <= MAX_VALUE(ARG) and in the INT case we check the exp and
mantissa bits. Returns a variable containing a boolean which has the result
of the check.
SEQ is the buffer to use to emit the gimple instructions into.
LOC is the location to use during fold calls. */
static tree
is_finite (gimple_seq *seq, tree arg, location_t loc)
{
tree type = TREE_TYPE (arg);
machine_mode mode = TYPE_MODE (type);
const tree bool_type = boolean_type_node;
if (!HONOR_NANS (arg) && !HONOR_INFINITIES (arg))
{
return build_int_cst (bool_type, true);
}
/* If not using optimized route then exit early. */
if (!use_ieee_int_mode (arg))
{
/* Perform IBM extended format fixups if required. */
perform_ibm_extended_fixups (&arg, &mode, &type, loc);
tree const isle_fn = builtin_decl_explicit (BUILT_IN_ISLESSEQUAL);
tree arg_p
= emit_tree_and_return_var (seq, fold_build1_loc (loc, ABS_EXPR, type,
arg));
REAL_VALUE_TYPE rmax;
char buf[128];
get_max_float (REAL_MODE_FORMAT (mode), buf, sizeof (buf));
real_from_string (&rmax, buf);
tree res = build_call_expr (isle_fn, 2, arg_p, build_real (type, rmax));
push_gimplify_context ();
gimplify_expr (&res, seq, NULL, is_gimple_val, fb_either);
pop_gimplify_context (NULL);
return emit_tree_and_return_var (seq, gimple_boolify(res));
}
const real_format *format = REAL_MODE_FORMAT (mode);
const HOST_WIDE_INT type_width = TYPE_PRECISION (type);
tree int_arg_type = build_nonstandard_integer_type (type_width, true);
/* This creates a mask to be used to check the exp value in the shifted
integer representation of the fpnum. */
const int exp_bits = (GET_MODE_SIZE (mode) * BITS_PER_UNIT) - format->p;
gcc_assert (format->p > 0);
tree significant_bit = build_int_cstu (int_arg_type, format->p);
tree exp_mask
= fold_build2_loc (loc, MINUS_EXPR, int_arg_type,
fold_build2_loc (loc, LSHIFT_EXPR, int_arg_type,
build_int_cstu (int_arg_type, 2),
build_int_cstu (int_arg_type,
exp_bits - 1)),
build_int_cstu (int_arg_type, 1));
/* Get the number reinterpreted as an integer.
Shift left to remove the sign. */
tree int_arg
= fold_build2_loc (loc, LSHIFT_EXPR, int_arg_type,
get_num_as_int (seq, arg, loc),
build_int_cstu (int_arg_type, 1));
/* This mask checks to see if the exp has all bits set and mantissa no
bits set. */
tree inf_mask
= fold_build2_loc (loc, LSHIFT_EXPR, int_arg_type,
exp_mask, significant_bit);
/* Check if exponent has all bits set and mantissa is 0. */
tree inf_check_tmp
= fold_build2_loc (loc, LT_EXPR, bool_type,
emit_tree_and_return_var (seq, int_arg),
inf_mask);
tree inf_check = emit_tree_and_return_var (seq, inf_check_tmp);
return emit_tree_and_return_var (seq, inf_check);
}
/* Generates code to check if ARG is a NaN. In the FP case we simply check if
ARG != ARG and in the INT case we check the bits in the exp and mantissa.
Returns a variable containing a boolean which has the result of the check.
SEQ is the buffer to use to emit the gimple instructions into.
LOC is the location to use during fold calls. */
static tree
is_nan (gimple_seq *seq, tree arg, location_t loc)
{
tree type = TREE_TYPE (arg);
machine_mode mode = TYPE_MODE (type);
const tree bool_type = boolean_type_node;
if (!HONOR_NANS (mode))
{
return build_int_cst (bool_type, false);
}
const real_format *format = REAL_MODE_FORMAT (mode);
/* If not using optimized route then exit early. */
if (!use_ieee_int_mode (arg))
{
/* Perform IBM extended format fixups if required. */
perform_ibm_extended_fixups (&arg, &mode, &type, loc);
tree arg_p
= emit_tree_and_return_var (seq, fold_build1_loc (loc, ABS_EXPR, type,
arg));
tree res
= fold_build2_loc (loc, UNORDERED_EXPR, bool_type,arg_p, arg_p);
return emit_tree_and_return_var (seq, res);
}
const HOST_WIDE_INT type_width = TYPE_PRECISION (type);
tree int_arg_type = build_nonstandard_integer_type (type_width, true);
/* This creates a mask to be used to check the exp value in the shifted
integer representation of the fpnum. */
const int exp_bits = (GET_MODE_SIZE (mode) * BITS_PER_UNIT) - format->p;
tree significant_bit = build_int_cstu (int_arg_type, format->p);
tree exp_mask
= fold_build2_loc (loc, MINUS_EXPR, int_arg_type,
fold_build2_loc (loc, LSHIFT_EXPR, int_arg_type,
build_int_cstu (int_arg_type, 2),
build_int_cstu (int_arg_type,
exp_bits - 1)),
build_int_cstu (int_arg_type, 1));
/* Get the number reinterpreted as an integer.
Shift left to remove the sign. */
tree int_arg
= fold_build2_loc (loc, LSHIFT_EXPR, int_arg_type,
get_num_as_int (seq, arg, loc),
build_int_cstu (int_arg_type, 1));
/* This mask checks to see if the exp has all bits set and mantissa no
bits set. */
tree inf_mask
= fold_build2_loc (loc, LSHIFT_EXPR, int_arg_type,
exp_mask, significant_bit);
/* Check if exponent has all bits set and mantissa is not 0. */
tree nan_check
= emit_tree_and_return_var(seq,
fold_build2_loc (loc, GT_EXPR, bool_type,
emit_tree_and_return_var(seq, int_arg),
inf_mask));
return emit_tree_and_return_var (seq, nan_check);
}
/* Validates a single argument from the arguments list CALL at position INDEX.
The extracted parameter is compared against the expected type CODE.
A boolean is returned indicating if the parameter exist and if of the
expected type. */
static bool
gimple_validate_arg (gimple* call, int index, enum tree_code code)
{
const tree arg = gimple_call_arg (call, index);
if (!arg)
return false;
else if (code == POINTER_TYPE)
return POINTER_TYPE_P (TREE_TYPE (arg));
else if (code == INTEGER_TYPE)
return INTEGRAL_TYPE_P (TREE_TYPE (arg));
return code == TREE_CODE (TREE_TYPE (arg));
}
/* Lowers calls to __builtin_fpclassify to
fpclassify (x) ->
isnormal(x) ? FP_NORMAL :
iszero (x) ? FP_ZERO :
isnan (x) ? FP_NAN :
isinfinite (x) ? FP_INFINITE :
FP_SUBNORMAL.
The code may use integer arithmentic if it decides
that the produced assembly would be faster. This can only be done
for numbers that are similar to IEEE-754 in format.
This builtin will generate code to return the appropriate floating
point classification depending on the value of the floating point
number passed in. The possible return values must be supplied as
int arguments to the call in the following order: FP_NAN, FP_INFINITE,
FP_NORMAL, FP_SUBNORMAL and FP_ZERO. The ellipses is for exactly
one floating point argument which is "type generic".
GSI is the gimple iterator containing the fpclassify call to lower.
The call will be expanded and replaced inline in the given GSI. */
static void
lower_builtin_fpclassify (gimple_stmt_iterator *gsi)
{
gimple *call = gsi_stmt (*gsi);
location_t loc = gimple_location (call);
/* Verify the required arguments in the original call. */
if (gimple_call_num_args (call) != 6
|| !gimple_validate_arg (call, 0, INTEGER_TYPE)
|| !gimple_validate_arg (call, 1, INTEGER_TYPE)
|| !gimple_validate_arg (call, 2, INTEGER_TYPE)
|| !gimple_validate_arg (call, 3, INTEGER_TYPE)
|| !gimple_validate_arg (call, 4, INTEGER_TYPE)
|| !gimple_validate_arg (call, 5, REAL_TYPE))
return;
/* Collect the arguments from the call. */
tree fp_nan = gimple_call_arg (call, 0);
tree fp_infinite = gimple_call_arg (call, 1);
tree fp_normal = gimple_call_arg (call, 2);
tree fp_subnormal = gimple_call_arg (call, 3);
tree fp_zero = gimple_call_arg (call, 4);
tree arg = gimple_call_arg (call, 5);
gimple_seq body = NULL;
/* Create label to jump to to exit. */
tree done_label = create_artificial_label (UNKNOWN_LOCATION);
tree dest;
tree orig_dest = dest = gimple_call_lhs (call);
if (orig_dest && TREE_CODE (orig_dest) == SSA_NAME)
dest = create_tmp_reg (TREE_TYPE (orig_dest));
emit_tree_cond (&body, dest, done_label,
is_normal (&body, arg, loc), fp_normal);
emit_tree_cond (&body, dest, done_label,
is_zero (&body, arg, loc), fp_zero);
emit_tree_cond (&body, dest, done_label,
is_nan (&body, arg, loc), fp_nan);
emit_tree_cond (&body, dest, done_label,
is_infinity (&body, arg, loc), fp_infinite);
/* And finally, emit the default case if nothing else matches.
This replaces the call to is_subnormal. */
gimple_seq_add_stmt (&body, gimple_build_assign (dest, fp_subnormal));
gimple_seq_add_stmt (&body, gimple_build_label (done_label));
/* Build orig_dest = dest if necessary. */
if (dest != orig_dest)
{
gimple_seq_add_stmt (&body, gimple_build_assign (orig_dest, dest));
}
gsi_insert_seq_before (gsi, body, GSI_SAME_STMT);
/* Remove the call to __builtin_fpclassify. */
gsi_remove (gsi, false);
}
/* Generic wrapper for the is_nan, is_normal, is_subnormal, is_zero, etc.
All these functions have the same setup. The wrapper validates the parameter
and also creates the branches and labels required to properly invoke.
This has been generalize and the function to call is passed as argument FNDECL.
GSI is the gimple iterator containing the fpclassify call to lower.
The call will be expanded and replaced inline in the given GSI. */
static void
gen_call_fp_builtin (gimple_stmt_iterator *gsi,
tree (*fndecl)(gimple_seq *, tree, location_t))
{
gimple *call = gsi_stmt (*gsi);
location_t loc = gimple_location (call);
/* Verify the required arguments in the original call. */
if (gimple_call_num_args (call) != 1
|| !gimple_validate_arg (call, 0, REAL_TYPE))
return;
tree arg = gimple_call_arg (call, 0);
gimple_seq body = NULL;
/* Create label to jump to to exit. */
tree done_label = create_artificial_label (UNKNOWN_LOCATION);
tree dest;
tree orig_dest = dest = gimple_call_lhs (call);
tree type = TREE_TYPE (orig_dest);
if (orig_dest && TREE_CODE (orig_dest) == SSA_NAME)
dest = create_tmp_reg (type);
tree t_true = build_int_cst (type, true);
tree t_false = build_int_cst (type, false);
emit_tree_cond (&body, dest, done_label,
fndecl (&body, arg, loc), t_true);
/* And finally, emit the default case if nothing else matches.
This replaces the call to false. */
gimple_seq_add_stmt (&body, gimple_build_assign (dest, t_false));
gimple_seq_add_stmt (&body, gimple_build_label (done_label));
/* Build orig_dest = dest if necessary. */
if (dest != orig_dest)
{
gimple_seq_add_stmt (&body, gimple_build_assign (orig_dest, dest));
}
gsi_insert_seq_before (gsi, body, GSI_SAME_STMT);
/* Remove the call to the builtin. */
gsi_remove (gsi, false);
}
/* Lower and expand calls to __builtin_isnan in GSI. */
static void
lower_builtin_isnan (gimple_stmt_iterator *gsi)
{
gen_call_fp_builtin (gsi, &is_nan);
}
/* Lower and expand calls to __builtin_isinfinite in GSI. */
static void
lower_builtin_isinfinite (gimple_stmt_iterator *gsi)
{
gen_call_fp_builtin (gsi, &is_infinity);
}
/* Lower and expand calls to __builtin_isnormal in GSI. */
static void
lower_builtin_isnormal (gimple_stmt_iterator *gsi)
{
gen_call_fp_builtin (gsi, &is_normal);
}
/* Lower and expand calls to __builtin_iszero in GSI. */
static void
lower_builtin_iszero (gimple_stmt_iterator *gsi)
{
gen_call_fp_builtin (gsi, &is_zero);
}
/* Lower and expand calls to __builtin_issubnormal in GSI. */
static void
lower_builtin_issubnormal (gimple_stmt_iterator *gsi)
{
gen_call_fp_builtin (gsi, &is_subnormal);
}
/* Lower and expand calls to __builtin_isfinite in GSI. */
static void
lower_builtin_isfinite (gimple_stmt_iterator *gsi)
{
gen_call_fp_builtin (gsi, &is_finite);
}
/* Lower calls to posix_memalign to
res = posix_memalign (ptr, align, size);
if (res == 0)

View File

@ -3052,6 +3052,7 @@ const struct real_format ieee_single_format =
true,
true,
false,
true,
"ieee_single"
};
@ -3075,6 +3076,7 @@ const struct real_format mips_single_format =
true,
false,
true,
true,
"mips_single"
};
@ -3098,6 +3100,7 @@ const struct real_format motorola_single_format =
true,
true,
true,
true,
"motorola_single"
};
@ -3132,6 +3135,7 @@ const struct real_format spu_single_format =
true,
false,
false,
false,
"spu_single"
};
@ -3343,6 +3347,7 @@ const struct real_format ieee_double_format =
true,
true,
false,
true,
"ieee_double"
};
@ -3366,6 +3371,7 @@ const struct real_format mips_double_format =
true,
false,
true,
true,
"mips_double"
};
@ -3389,6 +3395,7 @@ const struct real_format motorola_double_format =
true,
true,
true,
true,
"motorola_double"
};
@ -3735,6 +3742,7 @@ const struct real_format ieee_extended_motorola_format =
true,
true,
true,
false,
"ieee_extended_motorola"
};
@ -3758,6 +3766,7 @@ const struct real_format ieee_extended_intel_96_format =
true,
true,
false,
false,
"ieee_extended_intel_96"
};
@ -3781,6 +3790,7 @@ const struct real_format ieee_extended_intel_128_format =
true,
true,
false,
false,
"ieee_extended_intel_128"
};
@ -3806,6 +3816,7 @@ const struct real_format ieee_extended_intel_96_round_53_format =
true,
true,
false,
false,
"ieee_extended_intel_96_round_53"
};
@ -3896,6 +3907,7 @@ const struct real_format ibm_extended_format =
true,
true,
false,
false,
"ibm_extended"
};
@ -3919,6 +3931,7 @@ const struct real_format mips_extended_format =
true,
false,
true,
false,
"mips_extended"
};
@ -4184,6 +4197,7 @@ const struct real_format ieee_quad_format =
true,
true,
false,
true,
"ieee_quad"
};
@ -4207,6 +4221,7 @@ const struct real_format mips_quad_format =
true,
false,
true,
true,
"mips_quad"
};
@ -4509,6 +4524,7 @@ const struct real_format vax_f_format =
false,
false,
false,
false,
"vax_f"
};
@ -4532,6 +4548,7 @@ const struct real_format vax_d_format =
false,
false,
false,
false,
"vax_d"
};
@ -4555,6 +4572,7 @@ const struct real_format vax_g_format =
false,
false,
false,
false,
"vax_g"
};
@ -4633,6 +4651,7 @@ const struct real_format decimal_single_format =
true,
true,
false,
false,
"decimal_single"
};
@ -4657,6 +4676,7 @@ const struct real_format decimal_double_format =
true,
true,
false,
false,
"decimal_double"
};
@ -4681,6 +4701,7 @@ const struct real_format decimal_quad_format =
true,
true,
false,
false,
"decimal_quad"
};
@ -4820,6 +4841,7 @@ const struct real_format ieee_half_format =
true,
true,
false,
true,
"ieee_half"
};
@ -4846,6 +4868,7 @@ const struct real_format arm_half_format =
true,
false,
false,
false,
"arm_half"
};
@ -4893,6 +4916,7 @@ const struct real_format real_internal_format =
true,
true,
false,
false,
"real_internal"
};
@ -5080,6 +5104,16 @@ get_max_float (const struct real_format *fmt, char *buf, size_t len)
gcc_assert (strlen (buf) < len);
}
/* Write into BUF the minimum negative representable finite floating-point
number, x, such that b**(x-1) is normalized.
BUF must be large enough to contain the result. */
void
get_min_float (const struct real_format *fmt, char *buf, size_t len)
{
sprintf (buf, "0x1p%d", fmt->emin - 1);
gcc_assert (strlen (buf) < len);
}
/* True if mode M has a NaN representation and
the treatment of NaN operands is important. */

View File

@ -161,6 +161,19 @@ struct real_format
bool has_signed_zero;
bool qnan_msb_set;
bool canonical_nan_lsbs_set;
/* This flag indicates whether the format is suitable for the optimized
code paths for the __builtin_fpclassify function and friends. For
this, the format must be a base 2 representation with the sign bit as
the most-significant bit followed by (exp <= 32) exponent bits
followed by the mantissa bits. It must be possible to interpret the
bits of the floating-point representation as an integer. NaNs and
INFs (if available) must be represented by the same schema used by
IEEE 754. (NaNs must be represented by an exponent with all bits 1,
any mantissa except all bits 0 and any sign bit. +INF and -INF must be
represented by an exponent with all bits 1, a mantissa with all bits 0 and
a sign bit of 0 and 1 respectively.) */
bool is_binary_ieee_compatible;
const char *name;
};
@ -511,6 +524,11 @@ extern bool real_isinteger (const REAL_VALUE_TYPE *, HOST_WIDE_INT *);
float string. BUF must be large enough to contain the result. */
extern void get_max_float (const struct real_format *, char *, size_t);
/* Write into BUF the smallest positive normalized number x,
such that b**(x-1) is normalized. BUF must be large enough
to contain the result. */
extern void get_min_float (const struct real_format *, char *, size_t);
#ifndef GENERATOR_FILE
/* real related routines. */
extern wide_int real_to_integer (const REAL_VALUE_TYPE *, bool *, int);

View File

@ -1,3 +1,20 @@
2017-06-08 Tamar Christina <tamar.christina@arm.com>
* gcc.target/aarch64/builtin-fpclassify.c: New codegen test.
* gcc.dg/fold-notunord.c: Removed.
* gcc.dg/torture/floatn-tg-4.h: Add tests for iszero and issubnormal.
* gcc.dg/torture/float128-tg-4.c: Likewise.
* gcc.dg/torture/float128x-tg-4: Likewise.
* gcc.dg/torture/float16-tg-4.c: Likewise.
* gcc.dg/torture/float32-tg-4.c: Likewise.
* gcc.dg/torture/float32x-tg-4.c: Likewise.
* gcc.dg/torture/float64-tg-4.c: Likewise.
* gcc.dg/torture/float64x-tg-4.c: Likewise.
* gcc.dg/pr28796-1.c: Add -O2.
* gcc.dg/builtins-43.c: Check lower instead of gimple.
* gcc.dg/tg-tests.h: Add iszero and issubnormal.
* gcc.dg/pr77925.c: Add to test safe cases.
2017-06-08 Richard Biener <rguenther@suse.de>
PR tree-optimization/80928

View File

@ -1,5 +1,5 @@
/* { dg-do compile } */
/* { dg-options "-O1 -fno-trapping-math -fno-finite-math-only -fdump-tree-gimple -fdump-tree-optimized" } */
/* { dg-options "-O1 -fno-trapping-math -fno-finite-math-only -fdump-tree-lower -fdump-tree-optimized" } */
extern void f(int);
extern void link_error ();
@ -51,7 +51,7 @@ main ()
/* Check that all instances of __builtin_isnan were folded. */
/* { dg-final { scan-tree-dump-times "isnan" 0 "gimple" } } */
/* { dg-final { scan-tree-dump-times "isnan" 0 "lower" } } */
/* Check that all instances of link_error were subject to DCE. */
/* { dg-final { scan-tree-dump-times "link_error" 0 "optimized" } } */

View File

@ -1,9 +0,0 @@
/* { dg-do compile } */
/* { dg-options "-O -ftrapping-math -fdump-tree-optimized" } */
int f (double d)
{
return !__builtin_isnan (d);
}
/* { dg-final { scan-tree-dump " ord " "optimized" } } */

View File

@ -1,5 +1,5 @@
/* { dg-do link } */
/* { dg-options "-ffinite-math-only" } */
/* { dg-options "-ffinite-math-only -O2" } */
extern void link_error(void);

View File

@ -0,0 +1,11 @@
/* { dg-do run } */
/* { dg-options "-O2" } */
/* { dg-add-options ieee } */
/* { dg-skip-if "No Inf/NaN support" { spu-*-* } } */
#include "tg-tests.h"
int main(void)
{
return main_tests ();
}

View File

@ -11,6 +11,7 @@ void __attribute__ ((__noinline__))
foo_1 (float f, double d, long double ld,
int res_unord, int res_isnan, int res_isinf,
int res_isinf_sign, int res_isfin, int res_isnorm,
int res_iszero, int res_issubnorm,
int res_signbit, int classification)
{
if (__builtin_isunordered (f, 0) != res_unord)
@ -80,6 +81,29 @@ foo_1 (float f, double d, long double ld,
if (__builtin_finitel (ld) != res_isfin)
__builtin_abort ();
/* On CPUs which flush denormals to zero these tests can never work one
denormals for the floating point version of the implementation. The integer
versions would work fine but we can't detect which version we have here. */
#ifdef UNSAFE
if (!res_issubnorm) {
#endif
if (__builtin_iszero (f) != res_iszero)
__builtin_abort ();
if (__builtin_iszero (d) != res_iszero)
__builtin_abort ();
if (__builtin_iszero (ld) != res_iszero)
__builtin_abort ();
if (__builtin_issubnormal (f) != res_issubnorm)
__builtin_abort ();
if (__builtin_issubnormal (d) != res_issubnorm)
__builtin_abort ();
if (__builtin_issubnormal (ld) != res_issubnorm)
__builtin_abort ();
#ifdef UNSAFE
}
#endif
/* Sign bit of zeros and nans is not preserved in unsafe math mode. */
#ifdef UNSAFE
if (!res_isnan && f != 0 && d != 0 && ld != 0)
@ -115,12 +139,13 @@ foo_1 (float f, double d, long double ld,
void __attribute__ ((__noinline__))
foo (float f, double d, long double ld,
int res_unord, int res_isnan, int res_isinf,
int res_isfin, int res_isnorm, int classification)
int res_isfin, int res_isnorm, int res_iszero,
int res_issubnorm, int classification)
{
foo_1 (f, d, ld, res_unord, res_isnan, res_isinf, res_isinf, res_isfin, res_isnorm, 0, classification);
foo_1 (f, d, ld, res_unord, res_isnan, res_isinf, res_isinf, res_isfin, res_isnorm, res_iszero, res_issubnorm, 0, classification);
/* Try all the values negated as well. All will have the sign bit set,
except for the nan. */
foo_1 (-f, -d, -ld, res_unord, res_isnan, res_isinf, -res_isinf, res_isfin, res_isnorm, 1, classification);
foo_1 (-f, -d, -ld, res_unord, res_isnan, res_isinf, -res_isinf, res_isfin, res_isnorm, res_iszero, res_issubnorm, 1, classification);
}
int __attribute__ ((__noinline__))
@ -132,35 +157,35 @@ main_tests (void)
/* Test NaN. */
f = __builtin_nanf(""); d = __builtin_nan(""); ld = __builtin_nanl("");
foo(f, d, ld, /*unord=*/ 1, /*isnan=*/ 1, /*isinf=*/ 0, /*isfin=*/ 0, /*isnorm=*/ 0, FP_NAN);
foo(f, d, ld, /*unord=*/ 1, /*isnan=*/ 1, /*isinf=*/ 0, /*isfin=*/ 0, /*isnorm=*/ 0, /*iszero=*/0, /*issubnorm=*/0, FP_NAN);
/* Test infinity. */
f = __builtin_inff(); d = __builtin_inf(); ld = __builtin_infl();
foo(f, d, ld, /*unord=*/ 0, /*isnan=*/ 0, /*isinf=*/ 1, /*isfin=*/ 0, /*isnorm=*/ 0, FP_INFINITE);
foo(f, d, ld, /*unord=*/ 0, /*isnan=*/ 0, /*isinf=*/ 1, /*isfin=*/ 0, /*isnorm=*/ 0, /*iszero=*/0, /*issubnorm=*/0, FP_INFINITE);
/* Test zero. */
f = 0; d = 0; ld = 0;
foo(f, d, ld, /*unord=*/ 0, /*isnan=*/ 0, /*isinf=*/ 0, /*isfin=*/ 1, /*isnorm=*/ 0, FP_ZERO);
foo(f, d, ld, /*unord=*/ 0, /*isnan=*/ 0, /*isinf=*/ 0, /*isfin=*/ 1, /*isnorm=*/ 0, /*iszero=*/1, /*issubnorm=*/0, FP_ZERO);
/* Test one. */
f = 1; d = 1; ld = 1;
foo(f, d, ld, /*unord=*/ 0, /*isnan=*/ 0, /*isinf=*/ 0, /*isfin=*/ 1, /*isnorm=*/ 1, FP_NORMAL);
foo(f, d, ld, /*unord=*/ 0, /*isnan=*/ 0, /*isinf=*/ 0, /*isfin=*/ 1, /*isnorm=*/ 1, /*iszero=*/0, /*issubnorm=*/0, FP_NORMAL);
/* Test minimum values. */
f = __FLT_MIN__; d = __DBL_MIN__; ld = __LDBL_MIN__;
foo(f, d, ld, /*unord=*/ 0, /*isnan=*/ 0, /*isinf=*/ 0, /*isfin=*/ 1, /*isnorm=*/ 1, FP_NORMAL);
foo(f, d, ld, /*unord=*/ 0, /*isnan=*/ 0, /*isinf=*/ 0, /*isfin=*/ 1, /*isnorm=*/ 1, /*iszero=*/0, /*issubnorm=*/0, FP_NORMAL);
/* Test subnormal values. */
f = __FLT_MIN__/2; d = __DBL_MIN__/2; ld = __LDBL_MIN__/2;
foo(f, d, ld, /*unord=*/ 0, /*isnan=*/ 0, /*isinf=*/ 0, /*isfin=*/ 1, /*isnorm=*/ 0, FP_SUBNORMAL);
foo(f, d, ld, /*unord=*/ 0, /*isnan=*/ 0, /*isinf=*/ 0, /*isfin=*/ 1, /*isnorm=*/ 0, /*iszero=*/0, /*issubnorm=*/1, FP_SUBNORMAL);
/* Test maximum values. */
f = __FLT_MAX__; d = __DBL_MAX__; ld = __LDBL_MAX__;
foo(f, d, ld, /*unord=*/ 0, /*isnan=*/ 0, /*isinf=*/ 0, /*isfin=*/ 1, /*isnorm=*/ 1, FP_NORMAL);
foo(f, d, ld, /*unord=*/ 0, /*isnan=*/ 0, /*isinf=*/ 0, /*isfin=*/ 1, /*isnorm=*/ 1, /*iszero=*/0, /*issubnorm=*/0, FP_NORMAL);
/* Test overflow values. */
f = __FLT_MAX__*2; d = __DBL_MAX__*2; ld = __LDBL_MAX__*2;
foo(f, d, ld, /*unord=*/ 0, /*isnan=*/ 0, /*isinf=*/ 1, /*isfin=*/ 0, /*isnorm=*/ 0, FP_INFINITE);
foo(f, d, ld, /*unord=*/ 0, /*isnan=*/ 0, /*isinf=*/ 1, /*isfin=*/ 0, /*isnorm=*/ 0, /*iszero=*/0, /*issubnorm=*/0, FP_INFINITE);
return 0;
}

View File

@ -0,0 +1,11 @@
/* Test _Float128 type-generic built-in functions: __builtin_iszero,
__builtin_issubnormal. */
/* { dg-do run } */
/* { dg-options "" } */
/* { dg-add-options float128 } */
/* { dg-add-options ieee } */
/* { dg-require-effective-target float128_runtime } */
#define WIDTH 128
#define EXT 0
#include "floatn-tg-4.h"

View File

@ -0,0 +1,11 @@
/* Test _Float128x type-generic built-in functions: __builtin_iszero,
__builtin_issubnormal. */
/* { dg-do run } */
/* { dg-options "" } */
/* { dg-add-options float128x } */
/* { dg-add-options ieee } */
/* { dg-require-effective-target float128x_runtime } */
#define WIDTH 128
#define EXT 1
#include "floatn-tg-4.h"

View File

@ -0,0 +1,11 @@
/* Test _Float16 type-generic built-in functions: __builtin_iszero,
__builtin_issubnormal. */
/* { dg-do run } */
/* { dg-options "" } */
/* { dg-add-options float16 } */
/* { dg-add-options ieee } */
/* { dg-require-effective-target float16_runtime } */
#define WIDTH 16
#define EXT 0
#include "floatn-tg-4.h"

View File

@ -0,0 +1,11 @@
/* Test _Float32 type-generic built-in functions: __builtin_f__builtin_iszero,
__builtin_issubnormal. */
/* { dg-do run } */
/* { dg-options "" } */
/* { dg-add-options float32 } */
/* { dg-add-options ieee } */
/* { dg-require-effective-target float32_runtime } */
#define WIDTH 32
#define EXT 0
#include "floatn-tg-4.h"

View File

@ -0,0 +1,11 @@
/* Test _Float32x type-generic built-in functions: __builtin_iszero,
__builtin_issubnormal. */
/* { dg-do run } */
/* { dg-options "" } */
/* { dg-add-options float32x } */
/* { dg-add-options ieee } */
/* { dg-require-effective-target float32x_runtime } */
#define WIDTH 32
#define EXT 1
#include "floatn-tg-4.h"

View File

@ -0,0 +1,11 @@
/* Test _Float64 type-generic built-in functions: __builtin_iszero,
__builtin_issubnormal */
/* { dg-do run } */
/* { dg-options "" } */
/* { dg-add-options float64 } */
/* { dg-add-options ieee } */
/* { dg-require-effective-target float64_runtime } */
#define WIDTH 64
#define EXT 0
#include "floatn-tg-4.h"

View File

@ -0,0 +1,11 @@
/* Test _Float64x type-generic built-in functions: __builtin_iszero,
__builtin_issubnormal. */
/* { dg-do run } */
/* { dg-options "" } */
/* { dg-add-options float64x } */
/* { dg-add-options ieee } */
/* { dg-require-effective-target float64x_runtime } */
#define WIDTH 64
#define EXT 1
#include "floatn-tg-4.h"

View File

@ -0,0 +1,99 @@
/* Tests for _FloatN / _FloatNx types: compile and execution tests for
type-generic built-in functions: __builtin_iszero, __builtin_issubnormal.
Before including this file, define WIDTH as the value N; define EXT to 1
for _FloatNx and 0 for _FloatN. */
#define __STDC_WANT_IEC_60559_TYPES_EXT__
#include <float.h>
#define CONCATX(X, Y) X ## Y
#define CONCAT(X, Y) CONCATX (X, Y)
#define CONCAT3(X, Y, Z) CONCAT (CONCAT (X, Y), Z)
#define CONCAT4(W, X, Y, Z) CONCAT (CONCAT (CONCAT (W, X), Y), Z)
#if EXT
# define TYPE CONCAT3 (_Float, WIDTH, x)
# define CST(C) CONCAT4 (C, f, WIDTH, x)
# define MAX CONCAT3 (FLT, WIDTH, X_MAX)
# define MIN CONCAT3 (FLT, WIDTH, X_MIN)
# define TRUE_MIN CONCAT3 (FLT, WIDTH, X_TRUE_MIN)
#else
# define TYPE CONCAT (_Float, WIDTH)
# define CST(C) CONCAT3 (C, f, WIDTH)
# define MAX CONCAT3 (FLT, WIDTH, _MAX)
# define MIN CONCAT3 (FLT, WIDTH, _MIN)
# define TRUE_MIN CONCAT3 (FLT, WIDTH, _TRUE_MIN)
#endif
extern void exit (int);
extern void abort (void);
volatile TYPE inf = __builtin_inf (), nanval = __builtin_nan ("");
volatile TYPE neginf = -__builtin_inf (), negnanval = -__builtin_nan ("");
volatile TYPE zero = CST (0.0), negzero = -CST (0.0), one = CST (1.0);
volatile TYPE max = MAX, negmax = -MAX, min = MIN, negmin = -MIN;
volatile TYPE true_min = TRUE_MIN, negtrue_min = -TRUE_MIN;
volatile TYPE sub_norm = MIN / 2.0;
int
main (void)
{
if (__builtin_iszero (inf) == 1)
abort ();
if (__builtin_iszero (nanval) == 1)
abort ();
if (__builtin_iszero (neginf) == 1)
abort ();
if (__builtin_iszero (negnanval) == 1)
abort ();
if (__builtin_iszero (zero) != 1)
abort ();
if (__builtin_iszero (negzero) != 1)
abort ();
if (__builtin_iszero (one) == 1)
abort ();
if (__builtin_iszero (max) == 1)
abort ();
if (__builtin_iszero (negmax) == 1)
abort ();
if (__builtin_iszero (min) == 1)
abort ();
if (__builtin_iszero (negmin) == 1)
abort ();
if (__builtin_iszero (true_min) == 1)
abort ();
if (__builtin_iszero (negtrue_min) == 1)
abort ();
if (__builtin_iszero (sub_norm) == 1)
abort ();
if (__builtin_issubnormal (inf) == 1)
abort ();
if (__builtin_issubnormal (nanval) == 1)
abort ();
if (__builtin_issubnormal (neginf) == 1)
abort ();
if (__builtin_issubnormal (negnanval) == 1)
abort ();
if (__builtin_issubnormal (zero) == 1)
abort ();
if (__builtin_issubnormal (negzero) == 1)
abort ();
if (__builtin_issubnormal (one) == 1)
abort ();
if (__builtin_issubnormal (max) == 1)
abort ();
if (__builtin_issubnormal (negmax) == 1)
abort ();
if (__builtin_issubnormal (min) == 1)
abort ();
if (__builtin_issubnormal (negmin) == 1)
abort ();
if (__builtin_issubnormal (true_min) != 1)
abort ();
if (__builtin_issubnormal (negtrue_min) != 1)
abort ();
if (__builtin_issubnormal (sub_norm) != 1)
abort ();
exit (0);
}

View File

@ -0,0 +1,22 @@
/* This file checks the code generation for the new __builtin_fpclassify.
because checking the exact assembly isn't very useful, we'll just be checking
for the presence of certain instructions and the omition of others. */
/* { dg-options "-O2" } */
/* { dg-do compile } */
/* { dg-final { scan-assembler-not "\[ \t\]?fabs\[ \t\]?" } } */
/* { dg-final { scan-assembler-not "\[ \t\]?fcmp\[ \t\]?" } } */
/* { dg-final { scan-assembler-not "\[ \t\]?fcmpe\[ \t\]?" } } */
/* { dg-final { scan-assembler "\[ \t\]?ubfx\[ \t\]?" } } */
#include <stdio.h>
#include <math.h>
/*
fp_nan = args[0];
fp_infinite = args[1];
fp_normal = args[2];
fp_subnormal = args[3];
fp_zero = args[4];
*/
int f(double x) { return __builtin_fpclassify(0, 1, 4, 3, 2, x); }