diff options
author | Tamar Christina <tamar.christina@arm.com> | 2017-06-08 07:38:42 +0000 |
---|---|---|
committer | Tamar Christina <tnfchris@gcc.gnu.org> | 2017-06-08 07:38:42 +0000 |
commit | 94fc87ecdf45e2455deb7ee50e13765c98d4e904 (patch) | |
tree | 01e590119900d4a8b6df6a832bb5456d8a7fd03c /gcc/gimple-low.c | |
parent | fb4bc6ff6c08f4e5524fa5e1881b478b0f689155 (diff) | |
download | gcc-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.c | 910 |
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) |