summaryrefslogtreecommitdiff
path: root/gcc/fortran/trans-intrinsic.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/fortran/trans-intrinsic.c')
-rw-r--r--gcc/fortran/trans-intrinsic.c336
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. */