diff options
Diffstat (limited to 'gcc/fortran/trans-intrinsic.c')
-rw-r--r-- | gcc/fortran/trans-intrinsic.c | 336 |
1 files changed, 336 insertions, 0 deletions
diff --git a/gcc/fortran/trans-intrinsic.c b/gcc/fortran/trans-intrinsic.c index 0a3315d9cfa..b157b950ecc 100644 --- a/gcc/fortran/trans-intrinsic.c +++ b/gcc/fortran/trans-intrinsic.c @@ -7171,6 +7171,342 @@ conv_isocbinding_subroutine (gfc_code *code) } +/* Save and restore floating-point state. */ + +tree +gfc_save_fp_state (stmtblock_t *block) +{ + tree type, fpstate, tmp; + + type = build_array_type (char_type_node, + build_range_type (size_type_node, size_zero_node, + size_int (GFC_FPE_STATE_BUFFER_SIZE))); + fpstate = gfc_create_var (type, "fpstate"); + fpstate = gfc_build_addr_expr (pvoid_type_node, fpstate); + + tmp = build_call_expr_loc (input_location, gfor_fndecl_ieee_procedure_entry, + 1, fpstate); + gfc_add_expr_to_block (block, tmp); + + return fpstate; +} + + +void +gfc_restore_fp_state (stmtblock_t *block, tree fpstate) +{ + tree tmp; + + tmp = build_call_expr_loc (input_location, gfor_fndecl_ieee_procedure_exit, + 1, fpstate); + gfc_add_expr_to_block (block, tmp); +} + + +/* Generate code for arguments of IEEE functions. */ + +static void +conv_ieee_function_args (gfc_se *se, gfc_expr *expr, tree *argarray, + int nargs) +{ + gfc_actual_arglist *actual; + gfc_expr *e; + gfc_se argse; + int arg; + + actual = expr->value.function.actual; + for (arg = 0; arg < nargs; arg++, actual = actual->next) + { + gcc_assert (actual); + e = actual->expr; + + gfc_init_se (&argse, se); + gfc_conv_expr_val (&argse, e); + + gfc_add_block_to_block (&se->pre, &argse.pre); + gfc_add_block_to_block (&se->post, &argse.post); + argarray[arg] = argse.expr; + } +} + + +/* Generate code for intrinsics IEEE_IS_NAN, IEEE_IS_FINITE, + and IEEE_UNORDERED, which translate directly to GCC type-generic + built-ins. */ + +static void +conv_intrinsic_ieee_builtin (gfc_se * se, gfc_expr * expr, + enum built_in_function code, int nargs) +{ + tree args[2]; + gcc_assert ((unsigned) nargs <= sizeof(args)/sizeof(args[0])); + + conv_ieee_function_args (se, expr, args, nargs); + se->expr = build_call_expr_loc_array (input_location, + builtin_decl_explicit (code), + nargs, args); + STRIP_TYPE_NOPS (se->expr); + se->expr = fold_convert (gfc_typenode_for_spec (&expr->ts), se->expr); +} + + +/* Generate code for IEEE_IS_NORMAL intrinsic: + IEEE_IS_NORMAL(x) --> (__builtin_isnormal(x) || x == 0) */ + +static void +conv_intrinsic_ieee_is_normal (gfc_se * se, gfc_expr * expr) +{ + tree arg, isnormal, iszero; + + /* Convert arg, evaluate it only once. */ + conv_ieee_function_args (se, expr, &arg, 1); + arg = gfc_evaluate_now (arg, &se->pre); + + isnormal = build_call_expr_loc (input_location, + builtin_decl_explicit (BUILT_IN_ISNORMAL), + 1, arg); + iszero = fold_build2_loc (input_location, EQ_EXPR, boolean_type_node, arg, + build_real_from_int_cst (TREE_TYPE (arg), + integer_zero_node)); + se->expr = fold_build2_loc (input_location, TRUTH_OR_EXPR, + boolean_type_node, isnormal, iszero); + se->expr = fold_convert (gfc_typenode_for_spec (&expr->ts), se->expr); +} + + +/* Generate code for IEEE_IS_NEGATIVE intrinsic: + IEEE_IS_NEGATIVE(x) --> (__builtin_signbit(x) && !__builtin_isnan(x)) */ + +static void +conv_intrinsic_ieee_is_negative (gfc_se * se, gfc_expr * expr) +{ + tree arg, signbit, isnan, decl; + int argprec; + + /* Convert arg, evaluate it only once. */ + conv_ieee_function_args (se, expr, &arg, 1); + arg = gfc_evaluate_now (arg, &se->pre); + + isnan = build_call_expr_loc (input_location, + builtin_decl_explicit (BUILT_IN_ISNAN), + 1, arg); + STRIP_TYPE_NOPS (isnan); + + argprec = TYPE_PRECISION (TREE_TYPE (arg)); + decl = builtin_decl_for_precision (BUILT_IN_SIGNBIT, argprec); + signbit = build_call_expr_loc (input_location, decl, 1, arg); + signbit = fold_build2_loc (input_location, NE_EXPR, boolean_type_node, + signbit, integer_zero_node); + + se->expr = fold_build2_loc (input_location, TRUTH_AND_EXPR, + boolean_type_node, signbit, + fold_build1_loc (input_location, TRUTH_NOT_EXPR, + TREE_TYPE(isnan), isnan)); + + se->expr = fold_convert (gfc_typenode_for_spec (&expr->ts), se->expr); +} + + +/* Generate code for IEEE_LOGB and IEEE_RINT. */ + +static void +conv_intrinsic_ieee_logb_rint (gfc_se * se, gfc_expr * expr, + enum built_in_function code) +{ + tree arg, decl, call, fpstate; + int argprec; + + conv_ieee_function_args (se, expr, &arg, 1); + argprec = TYPE_PRECISION (TREE_TYPE (arg)); + decl = builtin_decl_for_precision (code, argprec); + + /* Save floating-point state. */ + fpstate = gfc_save_fp_state (&se->pre); + + /* Make the function call. */ + call = build_call_expr_loc (input_location, decl, 1, arg); + se->expr = fold_convert (gfc_typenode_for_spec (&expr->ts), call); + + /* Restore floating-point state. */ + gfc_restore_fp_state (&se->post, fpstate); +} + + +/* Generate code for IEEE_REM. */ + +static void +conv_intrinsic_ieee_rem (gfc_se * se, gfc_expr * expr) +{ + tree args[2], decl, call, fpstate; + int argprec; + + conv_ieee_function_args (se, expr, args, 2); + + /* If arguments have unequal size, convert them to the larger. */ + if (TYPE_PRECISION (TREE_TYPE (args[0])) + > TYPE_PRECISION (TREE_TYPE (args[1]))) + args[1] = fold_convert (TREE_TYPE (args[0]), args[1]); + else if (TYPE_PRECISION (TREE_TYPE (args[1])) + > TYPE_PRECISION (TREE_TYPE (args[0]))) + args[0] = fold_convert (TREE_TYPE (args[1]), args[0]); + + argprec = TYPE_PRECISION (TREE_TYPE (args[0])); + decl = builtin_decl_for_precision (BUILT_IN_REMAINDER, argprec); + + /* Save floating-point state. */ + fpstate = gfc_save_fp_state (&se->pre); + + /* Make the function call. */ + call = build_call_expr_loc_array (input_location, decl, 2, args); + se->expr = fold_convert (TREE_TYPE (args[0]), call); + + /* Restore floating-point state. */ + gfc_restore_fp_state (&se->post, fpstate); +} + + +/* Generate code for IEEE_NEXT_AFTER. */ + +static void +conv_intrinsic_ieee_next_after (gfc_se * se, gfc_expr * expr) +{ + tree args[2], decl, call, fpstate; + int argprec; + + conv_ieee_function_args (se, expr, args, 2); + + /* Result has the characteristics of first argument. */ + args[1] = fold_convert (TREE_TYPE (args[0]), args[1]); + argprec = TYPE_PRECISION (TREE_TYPE (args[0])); + decl = builtin_decl_for_precision (BUILT_IN_NEXTAFTER, argprec); + + /* Save floating-point state. */ + fpstate = gfc_save_fp_state (&se->pre); + + /* Make the function call. */ + call = build_call_expr_loc_array (input_location, decl, 2, args); + se->expr = fold_convert (TREE_TYPE (args[0]), call); + + /* Restore floating-point state. */ + gfc_restore_fp_state (&se->post, fpstate); +} + + +/* Generate code for IEEE_SCALB. */ + +static void +conv_intrinsic_ieee_scalb (gfc_se * se, gfc_expr * expr) +{ + tree args[2], decl, call, huge, type; + int argprec, n; + + conv_ieee_function_args (se, expr, args, 2); + + /* Result has the characteristics of first argument. */ + argprec = TYPE_PRECISION (TREE_TYPE (args[0])); + decl = builtin_decl_for_precision (BUILT_IN_SCALBN, argprec); + + if (TYPE_PRECISION (TREE_TYPE (args[1])) > TYPE_PRECISION (integer_type_node)) + { + /* We need to fold the integer into the range of a C int. */ + args[1] = gfc_evaluate_now (args[1], &se->pre); + type = TREE_TYPE (args[1]); + + n = gfc_validate_kind (BT_INTEGER, gfc_c_int_kind, false); + huge = gfc_conv_mpz_to_tree (gfc_integer_kinds[n].huge, + gfc_c_int_kind); + huge = fold_convert (type, huge); + args[1] = fold_build2_loc (input_location, MIN_EXPR, type, args[1], + huge); + args[1] = fold_build2_loc (input_location, MAX_EXPR, type, args[1], + fold_build1_loc (input_location, NEGATE_EXPR, + type, huge)); + } + + args[1] = fold_convert (integer_type_node, args[1]); + + /* Make the function call. */ + call = build_call_expr_loc_array (input_location, decl, 2, args); + se->expr = fold_convert (TREE_TYPE (args[0]), call); +} + + +/* Generate code for IEEE_COPY_SIGN. */ + +static void +conv_intrinsic_ieee_copy_sign (gfc_se * se, gfc_expr * expr) +{ + tree args[2], decl, sign; + int argprec; + + conv_ieee_function_args (se, expr, args, 2); + + /* Get the sign of the second argument. */ + argprec = TYPE_PRECISION (TREE_TYPE (args[1])); + decl = builtin_decl_for_precision (BUILT_IN_SIGNBIT, argprec); + sign = build_call_expr_loc (input_location, decl, 1, args[1]); + sign = fold_build2_loc (input_location, NE_EXPR, boolean_type_node, + sign, integer_zero_node); + + /* Create a value of one, with the right sign. */ + sign = fold_build3_loc (input_location, COND_EXPR, integer_type_node, + sign, + fold_build1_loc (input_location, NEGATE_EXPR, + integer_type_node, + integer_one_node), + integer_one_node); + args[1] = fold_convert (TREE_TYPE (args[0]), sign); + + argprec = TYPE_PRECISION (TREE_TYPE (args[0])); + decl = builtin_decl_for_precision (BUILT_IN_COPYSIGN, argprec); + + se->expr = build_call_expr_loc_array (input_location, decl, 2, args); +} + + +/* Generate code for an intrinsic function from the IEEE_ARITHMETIC + module. */ + +bool +gfc_conv_ieee_arithmetic_function (gfc_se * se, gfc_expr * expr) +{ + const char *name = expr->value.function.name; + +#define STARTS_WITH(A,B) (strncmp((A), (B), strlen(B)) == 0) + + if (STARTS_WITH (name, "_gfortran_ieee_is_nan")) + conv_intrinsic_ieee_builtin (se, expr, BUILT_IN_ISNAN, 1); + else if (STARTS_WITH (name, "_gfortran_ieee_is_finite")) + conv_intrinsic_ieee_builtin (se, expr, BUILT_IN_ISFINITE, 1); + else if (STARTS_WITH (name, "_gfortran_ieee_unordered")) + conv_intrinsic_ieee_builtin (se, expr, BUILT_IN_ISUNORDERED, 2); + else if (STARTS_WITH (name, "_gfortran_ieee_is_normal")) + conv_intrinsic_ieee_is_normal (se, expr); + else if (STARTS_WITH (name, "_gfortran_ieee_is_negative")) + conv_intrinsic_ieee_is_negative (se, expr); + else if (STARTS_WITH (name, "_gfortran_ieee_copy_sign")) + conv_intrinsic_ieee_copy_sign (se, expr); + else if (STARTS_WITH (name, "_gfortran_ieee_scalb")) + conv_intrinsic_ieee_scalb (se, expr); + else if (STARTS_WITH (name, "_gfortran_ieee_next_after")) + conv_intrinsic_ieee_next_after (se, expr); + else if (STARTS_WITH (name, "_gfortran_ieee_rem")) + conv_intrinsic_ieee_rem (se, expr); + else if (STARTS_WITH (name, "_gfortran_ieee_logb")) + conv_intrinsic_ieee_logb_rint (se, expr, BUILT_IN_LOGB); + else if (STARTS_WITH (name, "_gfortran_ieee_rint")) + conv_intrinsic_ieee_logb_rint (se, expr, BUILT_IN_RINT); + else + /* It is not among the functions we translate directly. We return + false, so a library function call is emitted. */ + return false; + +#undef STARTS_WITH + + return true; +} + + /* Generate code for an intrinsic function. Some map directly to library calls, others get special handling. In some cases the name of the function used depends on the type specifiers. */ |