aboutsummaryrefslogtreecommitdiff
path: root/gcc/gimple-low.c
diff options
context:
space:
mode:
authorTamar Christina <tamar.christina@arm.com>2017-06-08 07:38:42 +0000
committerTamar Christina <tnfchris@gcc.gnu.org>2017-06-08 07:38:42 +0000
commit94fc87ecdf45e2455deb7ee50e13765c98d4e904 (patch)
tree01e590119900d4a8b6df6a832bb5456d8a7fd03c /gcc/gimple-low.c
parentfb4bc6ff6c08f4e5524fa5e1881b478b0f689155 (diff)
downloadgcc-94fc87ecdf45e2455deb7ee50e13765c98d4e904.zip
gcc-94fc87ecdf45e2455deb7ee50e13765c98d4e904.tar.gz
gcc-94fc87ecdf45e2455deb7ee50e13765c98d4e904.tar.bz2
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
Diffstat (limited to 'gcc/gimple-low.c')
-rw-r--r--gcc/gimple-low.c910
1 files changed, 903 insertions, 7 deletions
diff --git a/gcc/gimple-low.c b/gcc/gimple-low.c
index 619b9d7..1cc4a4d 100644
--- a/gcc/gimple-low.c
+++ b/gcc/gimple-low.c
@@ -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)