re PR middle-end/77925 (Add __builtin_issubnormal)
authorTamar Christina <tamar.christina@arm.com>
Thu, 8 Jun 2017 07:38:42 +0000 (07:38 +0000)
committerTamar Christina <tnfchris@gcc.gnu.org>
Thu, 8 Jun 2017 07:38:42 +0000 (07:38 +0000)
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

23 files changed:
gcc/ChangeLog
gcc/builtins.c
gcc/builtins.def
gcc/c/c-typeck.c
gcc/doc/extend.texi
gcc/gimple-low.c
gcc/real.c
gcc/real.h
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/builtins-43.c
gcc/testsuite/gcc.dg/fold-notunord.c [deleted file]
gcc/testsuite/gcc.dg/pr28796-1.c
gcc/testsuite/gcc.dg/pr77925.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/tg-tests.h
gcc/testsuite/gcc.dg/torture/float128-tg-4.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/torture/float128x-tg-4.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/torture/float16-tg-4.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/torture/float32-tg-4.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/torture/float32x-tg-4.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/torture/float64-tg-4.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/torture/float64x-tg-4.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/torture/floatn-tg-4.h [new file with mode: 0644]
gcc/testsuite/gcc.target/aarch64/builtin-fpclassify.c [new file with mode: 0644]

index f07dc97d21edda0333053ea2dc2c9319c79ee3f4..15dbf0aebc98e5b7e2c14af85607f1b273064462 100644 (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
index 30462ad0f419721fd0aa2029dbc9f8f5593b5823..254dab6bfa18d4df0a39d114a22b29eb501257e6 100644 (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
index 58d78dbbdee58df77fb7bad904362327704403c5..db45c0771fb35faba6ae92e4e849a7285b6d0dde 100644 (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)
index 467552cef4be01d5a97311f8a2835cab3f66e348..92aab54d41621b3e3cc9184c22800ca6610717ce 100644 (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;
index d467a1652eeaba47140734796b1c75322527cd9f..7d3933539e794b54a3109d489a99222a434fd30d 100644 (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)
index 619b9d7bfb17e49d0d867504ba73a479f0f96cb0..1cc4a4dd699e30de53eb7fc50ca4b615d44d8da2 100644 (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)
index a5671b2865bd10db1d26bfa02cc592aa109a43e2..4b4635ff913319e7267af362690dbea66d229d19 100644 (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"
   };
 \f
@@ -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"
   };
 \f
@@ -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"
   };
 \f
@@ -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"
   };
 \f
@@ -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"
   };
 \f
@@ -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"
   };
 \f
@@ -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"
   };
 \f
@@ -4893,6 +4916,7 @@ const struct real_format real_internal_format =
     true,
     true,
     false,
+    false,
     "real_internal"
   };
 \f
@@ -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.  */
 
index e7248e30fa5075f61903dcededc39aa9bb86bc33..67fa7932e884d5102b80d8b16bfec6533054acbc 100644 (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);
index 52a0854bb1ff8bb0ee13606e197cc63c023337e4..3899e20dedd5e65e10242b183e389a619864cef9 100644 (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
index f7c318edf084104b9b820e18e631ed61e760569e..5d41c28aef8619f06658f45846ae15dd8b4987ed 100644 (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" } } */
diff --git a/gcc/testsuite/gcc.dg/fold-notunord.c b/gcc/testsuite/gcc.dg/fold-notunord.c
deleted file mode 100644 (file)
index ca34515..0000000
+++ /dev/null
@@ -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" } } */
index 077118a298878441e812410f3a6bf3707fb1d839..a57b4e350af1bc45344106fdeab4b32ef87f233f 100644 (file)
@@ -1,5 +1,5 @@
 /* { dg-do link } */
-/* { dg-options "-ffinite-math-only" } */
+/* { dg-options "-ffinite-math-only -O2" } */
 
 extern void link_error(void);
 
diff --git a/gcc/testsuite/gcc.dg/pr77925.c b/gcc/testsuite/gcc.dg/pr77925.c
new file mode 100644 (file)
index 0000000..f92518b
--- /dev/null
@@ -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 ();
+}
index 0cf1f6452584962cebda999b5e8c7a61180d7830..134bc8252750fbfc05f939030b2996a7c6f9ce9b 100644 (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;
 }
diff --git a/gcc/testsuite/gcc.dg/torture/float128-tg-4.c b/gcc/testsuite/gcc.dg/torture/float128-tg-4.c
new file mode 100644 (file)
index 0000000..ec9d3ad
--- /dev/null
@@ -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"
diff --git a/gcc/testsuite/gcc.dg/torture/float128x-tg-4.c b/gcc/testsuite/gcc.dg/torture/float128x-tg-4.c
new file mode 100644 (file)
index 0000000..0ede861
--- /dev/null
@@ -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"
diff --git a/gcc/testsuite/gcc.dg/torture/float16-tg-4.c b/gcc/testsuite/gcc.dg/torture/float16-tg-4.c
new file mode 100644 (file)
index 0000000..007c4c2
--- /dev/null
@@ -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"
diff --git a/gcc/testsuite/gcc.dg/torture/float32-tg-4.c b/gcc/testsuite/gcc.dg/torture/float32-tg-4.c
new file mode 100644 (file)
index 0000000..c7f8353
--- /dev/null
@@ -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"
diff --git a/gcc/testsuite/gcc.dg/torture/float32x-tg-4.c b/gcc/testsuite/gcc.dg/torture/float32x-tg-4.c
new file mode 100644 (file)
index 0000000..0d7a592
--- /dev/null
@@ -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"
diff --git a/gcc/testsuite/gcc.dg/torture/float64-tg-4.c b/gcc/testsuite/gcc.dg/torture/float64-tg-4.c
new file mode 100644 (file)
index 0000000..bb25a22
--- /dev/null
@@ -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"
diff --git a/gcc/testsuite/gcc.dg/torture/float64x-tg-4.c b/gcc/testsuite/gcc.dg/torture/float64x-tg-4.c
new file mode 100644 (file)
index 0000000..82305d9
--- /dev/null
@@ -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"
diff --git a/gcc/testsuite/gcc.dg/torture/floatn-tg-4.h b/gcc/testsuite/gcc.dg/torture/floatn-tg-4.h
new file mode 100644 (file)
index 0000000..aa3448c
--- /dev/null
@@ -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);
+}
diff --git a/gcc/testsuite/gcc.target/aarch64/builtin-fpclassify.c b/gcc/testsuite/gcc.target/aarch64/builtin-fpclassify.c
new file mode 100644 (file)
index 0000000..3a1bf95
--- /dev/null
@@ -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); }