Caveat 1: Patch is against svn 280157. Caveat 2: I am no longer subscribed to fortran@. Hit reply-all if you want to communicate with me.
Caveat 3: I don't do git. This is likely my last patch for GCC. TL;DR version. Fix the simplification and handling of the degree trigonometric functions. This includes fixing a number of ICEs. See PR 93871. ChangeLog and patch attached. Long version. This patch fixes a number of issues with the degree trigonometric functions. 1. The option -fdec-math is a now a NOP. I left the option intact, because someone may have it in a Makefile or build script. This option sets the variable flag_dec_math, which is no longer used. The DEC functions SIND, COSD, TAND, ASIND, ACOSD, ATAND, ATAN2D, COTAN, and COTAND are now part of the nonstandard intrinsics provided under -std=gnu. 2. The previous special handling of these functions has been removed. All functions are now handled just like any other intrinsic function (except for a few special cases with IEEE_ARITHMETIC). 3. Simplification routines do the following mappings: sind(x) = sin((pi/180) * x) asind(x) = (180/pi) * asin(x) cosd(x) = cos((pi/180) * x) acosd(x) = (180/pi) * acos(x) tand(x) = tan((pi/180) * x) atand(x) = (180/pi) * atan(x) atan2d(y,x) = (180/pi) * atan2(y,x) cotand(x) = cotan((pi/180) * x) All computations are carried out by MPFR or MPC. 4. For asind, acosd, atand, atan2d, cotan, and cotand are in-lined. For example, function foo(x) real foo, x foo = asind(x) end function foo produces foo (real(kind=4) & restrict x) { real(kind=4) __result_foo; __result_foo = __builtin_asinf (*x) * 5.72957763671875e+1; return __result_foo; } At the moment, cotand(x) is transformed into -tan((pi/180)*x + pi/2). No attempted of doing a range-reduction step (see below) has been done. For cotan(x) the argument can be real or complex. If x is real, the function is transformed as cotan(x) = -tan(x + pi/2). If x is complex, gfortran will produce cotan(x) = cos(x)/sin(x) where cos and sin map to __builtin_ccos and __builtin_csin, respectively. 5. New functions have been added to libgfortran to handle sind, cosd, and tand. For x = +-inf or nan, these functions return nan. For small x, sind(x) = sin((pi/180) * x) = (pi/180) * x tand(x) = tan((pi/180) * x) = (pi/180) * x cosd(x) = 1 - tiny (where tiny is volatile and should cause INEXACT). For normal x, a few steps are performed. First, x is replaced by fmod(x,360) such that the result is now in [0,360]. The functions are cyclic afterall. Second, [0,360] is folded into the range [0,45] and the choice of computing sin((pi/180)*x) or cos((pi/180)*x) is then made. This is required to achieve a small ULP near the zeros of the functions. A final step uses an FMA where (pi/180) is split into high and low parts. -- steve -- Steve
Fortran Frontend Changes: * gfortran.h: Add GFC_ISYM_ACOSD, GFC_ISYM_ASIND, GFC_ISYM_ATAN2D, GFC_ISYM_ATAND, GFC_ISYM_COSD, GFC_ISYM_COTAND, GFC_ISYM_SIND, and GFC_ISYM_TAND. * intrinsic.c (add_functions): Remove check for flag_dec_math. Give degree trig functions simplification and name resolution functions (e.g., gfc_simplify_atrigd () and gfc_resolve_atrigd ()). (do_simplify): Remove special casing of degree trig functions. * intrinsic.h (gfc_simplify_acosd, gfc_simplify_asind, gfc_simplify_atand, gfc_simplify_cosd, gfc_simplify_cotand, gfc_simplify_sind, gfc_simplify_tand, gfc_resolve_trigd2): Add new prototypes. (gfc_simplify_atrigd, gfc_simplify_trigd, gfc_resolve_cotan, resolve_atrigd): Remove prototypes of deleted functions. * iresolve.c (is_trig_resolved, copy_replace_function_shallow, gfc_resolve_cotan, get_radians, get_degrees, resolve_trig_call, gfc_resolve_atrigd, gfc_resolve_atan2d): Delete functions. (gfc_resolve_trigd, gfc_resolve_trigd2): Set typ-spec and resolve funciton names. * simplify.c (rad2deg): Rename degrees_f (). * simplify.c (rad2deg, gfc_simplify_acosd, gfc_simplify_asind, gfc_simplify_atand, gfc_simplify_atan2d, gfc_simplify_cosd, gfc_simplify_sind, gfc_simplify_tand, gfc_simplify_cotand): New function. (gfc_simplify_atan2): Fix error message. (simplify_trig_call, gfc_simplify_trigd, gfc_simplify_atrigd, radians_f): Delete function. * trans-intrinsic.c: Add LIB_FUNCTION () for sind, cosd, tand. (rad2deg, gfc_conv_intrinsic_atrigd, gfc_conv_intrinsic_cotan, gfc_conv_intrinsic_cotand, gfc_conv_intrinsic_atan2d): New function Libgfortran Changes: * Makefile.am: Add intrinsics/trigd.c to build. * Makefile.in: Regenerated via --enable-maintainer-mode. * gfortran.map: Add symbols to shared library symbol map. * intrinsics/trigd.c: Implementations for sind (), cosd() and tand (). * intrinsics/trigd_inc.c: Internals for trigd.c.
Index: gcc/fortran/gfortran.h =================================================================== --- gcc/fortran/gfortran.h (revision 280157) +++ gcc/fortran/gfortran.h (working copy) @@ -357,6 +357,7 @@ enum gfc_isym_id GFC_ISYM_ACCESS, GFC_ISYM_ACHAR, GFC_ISYM_ACOS, + GFC_ISYM_ACOSD, GFC_ISYM_ACOSH, GFC_ISYM_ADJUSTL, GFC_ISYM_ADJUSTR, @@ -369,10 +370,13 @@ enum gfc_isym_id GFC_ISYM_ANINT, GFC_ISYM_ANY, GFC_ISYM_ASIN, + GFC_ISYM_ASIND, GFC_ISYM_ASINH, GFC_ISYM_ASSOCIATED, GFC_ISYM_ATAN, GFC_ISYM_ATAN2, + GFC_ISYM_ATAN2D, + GFC_ISYM_ATAND, GFC_ISYM_ATANH, GFC_ISYM_ATOMIC_ADD, GFC_ISYM_ATOMIC_AND, @@ -410,8 +414,10 @@ enum gfc_isym_id GFC_ISYM_CONJG, GFC_ISYM_CONVERSION, GFC_ISYM_COS, + GFC_ISYM_COSD, GFC_ISYM_COSH, GFC_ISYM_COTAN, + GFC_ISYM_COTAND, GFC_ISYM_COUNT, GFC_ISYM_CPU_TIME, GFC_ISYM_CSHIFT, @@ -598,6 +604,7 @@ enum gfc_isym_id GFC_ISYM_SIGNAL, GFC_ISYM_SI_KIND, GFC_ISYM_SIN, + GFC_ISYM_SIND, GFC_ISYM_SINH, GFC_ISYM_SIZE, GFC_ISYM_SLEEP, @@ -618,6 +625,7 @@ enum gfc_isym_id GFC_ISYM_SYSTEM, GFC_ISYM_SYSTEM_CLOCK, GFC_ISYM_TAN, + GFC_ISYM_TAND, GFC_ISYM_TANH, GFC_ISYM_TEAM_NUMBER, GFC_ISYM_THIS_IMAGE, Index: gcc/fortran/intrinsic.c =================================================================== --- gcc/fortran/intrinsic.c (revision 280157) +++ gcc/fortran/intrinsic.c (working copy) @@ -3281,117 +3281,121 @@ add_functions (void) make_generic ("loc", GFC_ISYM_LOC, GFC_STD_GNU); - if (flag_dec_math) - { - add_sym_1 ("acosd", GFC_ISYM_ACOS, CLASS_ELEMENTAL, ACTUAL_YES, BT_REAL, - dr, GFC_STD_GNU, - gfc_check_fn_r, gfc_simplify_atrigd, gfc_resolve_atrigd, - x, BT_REAL, dr, REQUIRED); - add_sym_1 ("dacosd", GFC_ISYM_ACOS, CLASS_ELEMENTAL, ACTUAL_YES, BT_REAL, - dd, GFC_STD_GNU, - gfc_check_fn_d, gfc_simplify_atrigd, gfc_resolve_atrigd, - x, BT_REAL, dd, REQUIRED); + /* The next of intrinsic subprogram are the degree trignometric functions. + These were hidden behind the -fdec-math option, but are now simply + included as extensions to the set of intrinsic subprograms. */ - make_generic ("acosd", GFC_ISYM_ACOS, GFC_STD_GNU); + add_sym_1 ("acosd", GFC_ISYM_ACOSD, CLASS_ELEMENTAL, ACTUAL_YES, + BT_REAL, dr, GFC_STD_GNU, + gfc_check_fn_r, gfc_simplify_acosd, gfc_resolve_trigd, + x, BT_REAL, dr, REQUIRED); - add_sym_1 ("asind", GFC_ISYM_ASIN, CLASS_ELEMENTAL, ACTUAL_YES, BT_REAL, - dr, GFC_STD_GNU, - gfc_check_fn_r, gfc_simplify_atrigd, gfc_resolve_atrigd, - x, BT_REAL, dr, REQUIRED); + add_sym_1 ("dacosd", GFC_ISYM_ACOSD, CLASS_ELEMENTAL, ACTUAL_YES, + BT_REAL, dd, GFC_STD_GNU, + gfc_check_fn_d, gfc_simplify_acosd, gfc_resolve_trigd, + x, BT_REAL, dd, REQUIRED); - add_sym_1 ("dasind", GFC_ISYM_ASIN, CLASS_ELEMENTAL, ACTUAL_YES, BT_REAL, - dd, GFC_STD_GNU, - gfc_check_fn_d, gfc_simplify_atrigd, gfc_resolve_atrigd, - x, BT_REAL, dd, REQUIRED); + make_generic ("acosd", GFC_ISYM_ACOSD, GFC_STD_GNU); - make_generic ("asind", GFC_ISYM_ASIN, GFC_STD_GNU); + add_sym_1 ("asind", GFC_ISYM_ASIND, CLASS_ELEMENTAL, ACTUAL_YES, + BT_REAL, dr, GFC_STD_GNU, + gfc_check_fn_r, gfc_simplify_asind, gfc_resolve_trigd, + x, BT_REAL, dr, REQUIRED); - add_sym_1 ("atand", GFC_ISYM_ATAN, CLASS_ELEMENTAL, ACTUAL_YES, BT_REAL, - dr, GFC_STD_GNU, - gfc_check_fn_r, gfc_simplify_atrigd, gfc_resolve_atrigd, - x, BT_REAL, dr, REQUIRED); + add_sym_1 ("dasind", GFC_ISYM_ASIND, CLASS_ELEMENTAL, ACTUAL_YES, + BT_REAL, dd, GFC_STD_GNU, + gfc_check_fn_d, gfc_simplify_asind, gfc_resolve_trigd, + x, BT_REAL, dd, REQUIRED); - add_sym_1 ("datand", GFC_ISYM_ATAN, CLASS_ELEMENTAL, ACTUAL_YES, BT_REAL, - dd, GFC_STD_GNU, - gfc_check_fn_d, gfc_simplify_atrigd, gfc_resolve_atrigd, - x, BT_REAL, dd, REQUIRED); + make_generic ("asind", GFC_ISYM_ASIND, GFC_STD_GNU); - make_generic ("atand", GFC_ISYM_ATAN, GFC_STD_GNU); + add_sym_1 ("atand", GFC_ISYM_ATAND, CLASS_ELEMENTAL, ACTUAL_YES, + BT_REAL, dr, GFC_STD_GNU, + gfc_check_fn_r, gfc_simplify_atand, gfc_resolve_trigd, + x, BT_REAL, dr, REQUIRED); - add_sym_2 ("atan2d",GFC_ISYM_ATAN2,CLASS_ELEMENTAL, ACTUAL_YES, BT_REAL, - dr, GFC_STD_GNU, - gfc_check_atan2, gfc_simplify_atan2d, gfc_resolve_atan2d, - y, BT_REAL, dr, REQUIRED, x, BT_REAL, dr, REQUIRED); + add_sym_1 ("datand", GFC_ISYM_ATAND, CLASS_ELEMENTAL, ACTUAL_YES, + BT_REAL, dd, GFC_STD_GNU, + gfc_check_fn_d, gfc_simplify_atand, gfc_resolve_trigd, + x, BT_REAL, dd, REQUIRED); - add_sym_2 ("datan2d",GFC_ISYM_ATAN2,CLASS_ELEMENTAL, ACTUAL_YES, BT_REAL, - dd, GFC_STD_GNU, - gfc_check_datan2, gfc_simplify_atan2d, gfc_resolve_atan2d, - y, BT_REAL, dd, REQUIRED, x, BT_REAL, dd, REQUIRED); + make_generic ("atand", GFC_ISYM_ATAND, GFC_STD_GNU); - make_generic ("atan2d", GFC_ISYM_ATAN2, GFC_STD_GNU); + add_sym_2 ("atan2d", GFC_ISYM_ATAN2D, CLASS_ELEMENTAL, ACTUAL_YES, + BT_REAL, dr, GFC_STD_GNU, + gfc_check_atan2, gfc_simplify_atan2d, gfc_resolve_trigd2, + y, BT_REAL, dr, REQUIRED, + x, BT_REAL, dr, REQUIRED); - add_sym_1 ("cosd", GFC_ISYM_COS, CLASS_ELEMENTAL, ACTUAL_YES, BT_REAL, - dr, GFC_STD_GNU, - gfc_check_fn_r, gfc_simplify_trigd, gfc_resolve_trigd, - x, BT_REAL, dr, REQUIRED); + add_sym_2 ("datan2d", GFC_ISYM_ATAN2D, CLASS_ELEMENTAL, ACTUAL_YES, + BT_REAL, dd, GFC_STD_GNU, + gfc_check_datan2, gfc_simplify_atan2d, gfc_resolve_trigd2, + y, BT_REAL, dd, REQUIRED, + x, BT_REAL, dd, REQUIRED); - add_sym_1 ("dcosd", GFC_ISYM_COS, CLASS_ELEMENTAL, ACTUAL_YES, BT_REAL, - dd, GFC_STD_GNU, - gfc_check_fn_d, gfc_simplify_trigd, gfc_resolve_trigd, - x, BT_REAL, dd, REQUIRED); + make_generic ("atan2d", GFC_ISYM_ATAN2D, GFC_STD_GNU); - make_generic ("cosd", GFC_ISYM_COS, GFC_STD_GNU); + add_sym_1 ("cosd", GFC_ISYM_COSD, CLASS_ELEMENTAL, ACTUAL_YES, + BT_REAL, dr, GFC_STD_GNU, + gfc_check_fn_r, gfc_simplify_cosd, gfc_resolve_trigd, + x, BT_REAL, dr, REQUIRED); - add_sym_1 ("cotan", GFC_ISYM_COTAN, CLASS_ELEMENTAL, ACTUAL_YES, BT_REAL, - dr, GFC_STD_GNU, - gfc_check_fn_rc2008, gfc_simplify_cotan, gfc_resolve_cotan, - x, BT_REAL, dr, REQUIRED); + add_sym_1 ("dcosd", GFC_ISYM_COSD, CLASS_ELEMENTAL, ACTUAL_YES, + BT_REAL, dd, GFC_STD_GNU, + gfc_check_fn_d, gfc_simplify_cosd, gfc_resolve_trigd, + x, BT_REAL, dd, REQUIRED); - add_sym_1 ("dcotan", GFC_ISYM_COTAN, CLASS_ELEMENTAL, ACTUAL_YES, BT_REAL, - dd, GFC_STD_GNU, - gfc_check_fn_d, gfc_simplify_cotan, gfc_resolve_cotan, - x, BT_REAL, dd, REQUIRED); + make_generic ("cosd", GFC_ISYM_COSD, GFC_STD_GNU); - make_generic ("cotan", GFC_ISYM_COTAN, GFC_STD_GNU); + add_sym_1 ("cotan", GFC_ISYM_COTAN, CLASS_ELEMENTAL, ACTUAL_YES, + BT_REAL, dr, GFC_STD_GNU, + gfc_check_fn_rc2008, gfc_simplify_cotan, gfc_resolve_trigd, + x, BT_REAL, dr, REQUIRED); - add_sym_1 ("cotand", GFC_ISYM_COTAN, CLASS_ELEMENTAL, ACTUAL_YES, BT_REAL, - dr, GFC_STD_GNU, - gfc_check_fn_r, gfc_simplify_trigd, gfc_resolve_trigd, - x, BT_REAL, dr, REQUIRED); + add_sym_1 ("dcotan", GFC_ISYM_COTAN, CLASS_ELEMENTAL, ACTUAL_YES, + BT_REAL, dd, GFC_STD_GNU, + gfc_check_fn_d, gfc_simplify_cotan, gfc_resolve_trigd, + x, BT_REAL, dd, REQUIRED); - add_sym_1 ("dcotand",GFC_ISYM_COTAN, CLASS_ELEMENTAL, ACTUAL_YES, BT_REAL, - dd, GFC_STD_GNU, - gfc_check_fn_d, gfc_simplify_trigd, gfc_resolve_trigd, - x, BT_REAL, dd, REQUIRED); + make_generic ("cotan", GFC_ISYM_COTAN, GFC_STD_GNU); - make_generic ("cotand", GFC_ISYM_COTAN, GFC_STD_GNU); + add_sym_1 ("cotand", GFC_ISYM_COTAND, CLASS_ELEMENTAL, ACTUAL_YES, + BT_REAL, dr, GFC_STD_GNU, + gfc_check_fn_r, gfc_simplify_cotand, gfc_resolve_trigd, + x, BT_REAL, dr, REQUIRED); - add_sym_1 ("sind", GFC_ISYM_SIN, CLASS_ELEMENTAL, ACTUAL_YES, BT_REAL, - dr, GFC_STD_GNU, - gfc_check_fn_r, gfc_simplify_trigd, gfc_resolve_trigd, - x, BT_REAL, dr, REQUIRED); + add_sym_1 ("dcotand",GFC_ISYM_COTAND, CLASS_ELEMENTAL, ACTUAL_YES, + BT_REAL, dd, GFC_STD_GNU, + gfc_check_fn_d, gfc_simplify_cotand, gfc_resolve_trigd, + x, BT_REAL, dd, REQUIRED); - add_sym_1 ("dsind", GFC_ISYM_SIN, CLASS_ELEMENTAL, ACTUAL_YES, BT_REAL, - dd, GFC_STD_GNU, - gfc_check_fn_d, gfc_simplify_trigd, gfc_resolve_trigd, - x, BT_REAL, dd, REQUIRED); + make_generic ("cotand", GFC_ISYM_COTAND, GFC_STD_GNU); - make_generic ("sind", GFC_ISYM_SIN, GFC_STD_GNU); + add_sym_1 ("sind", GFC_ISYM_SIND, CLASS_ELEMENTAL, ACTUAL_YES, + BT_REAL, dr, GFC_STD_GNU, + gfc_check_fn_r, gfc_simplify_sind, gfc_resolve_trigd, + x, BT_REAL, dr, REQUIRED); - add_sym_1 ("tand", GFC_ISYM_TAN, CLASS_ELEMENTAL, ACTUAL_YES, BT_REAL, - dr, GFC_STD_GNU, - gfc_check_fn_r, gfc_simplify_trigd, gfc_resolve_trigd, - x, BT_REAL, dr, REQUIRED); + add_sym_1 ("dsind", GFC_ISYM_SIND, CLASS_ELEMENTAL, ACTUAL_YES, + BT_REAL, dd, GFC_STD_GNU, + gfc_check_fn_d, gfc_simplify_sind, gfc_resolve_trigd, + x, BT_REAL, dd, REQUIRED); - add_sym_1 ("dtand", GFC_ISYM_TAN, CLASS_ELEMENTAL, ACTUAL_YES, BT_REAL, - dd, GFC_STD_GNU, - gfc_check_fn_d, gfc_simplify_trigd, gfc_resolve_trigd, - x, BT_REAL, dd, REQUIRED); + make_generic ("sind", GFC_ISYM_SIND, GFC_STD_GNU); - make_generic ("tand", GFC_ISYM_TAN, GFC_STD_GNU); - } + add_sym_1 ("tand", GFC_ISYM_TAND, CLASS_ELEMENTAL, ACTUAL_YES, + BT_REAL, dr, GFC_STD_GNU, + gfc_check_fn_r, gfc_simplify_tand, gfc_resolve_trigd, + x, BT_REAL, dr, REQUIRED); + add_sym_1 ("dtand", GFC_ISYM_TAND, CLASS_ELEMENTAL, ACTUAL_YES, + BT_REAL, dd, GFC_STD_GNU, + gfc_check_fn_d, gfc_simplify_tand, gfc_resolve_trigd, + x, BT_REAL, dd, REQUIRED); + + make_generic ("tand", GFC_ISYM_TAND, GFC_STD_GNU); + /* The following function is internally used for coarray libray functions. "make_from_module" makes it inaccessible for external users. */ add_sym_1 (GFC_PREFIX ("caf_get"), GFC_ISYM_CAF_GET, CLASS_IMPURE, ACTUAL_NO, @@ -4563,15 +4567,6 @@ do_simplify (gfc_intrinsic_sym *specific, gfc_expr *e) if (specific->simplify.f1 == gfc_simplify_max) { result = gfc_simplify_max (e); - goto finish; - } - - /* Some math intrinsics need to wrap the original expression. */ - if (specific->simplify.f1 == gfc_simplify_trigd - || specific->simplify.f1 == gfc_simplify_atrigd - || specific->simplify.f1 == gfc_simplify_cotan) - { - result = (*specific->simplify.f1) (e); goto finish; } Index: gcc/fortran/intrinsic.h =================================================================== --- gcc/fortran/intrinsic.h (revision 280157) +++ gcc/fortran/intrinsic.h (working copy) @@ -237,13 +237,14 @@ bool gfc_check_unlink_sub (gfc_expr *, gfc_expr *); gfc_expr *gfc_simplify_abs (gfc_expr *); gfc_expr *gfc_simplify_achar (gfc_expr *, gfc_expr *); gfc_expr *gfc_simplify_acos (gfc_expr *); +gfc_expr *gfc_simplify_acosd (gfc_expr *); gfc_expr *gfc_simplify_acosh (gfc_expr *); gfc_expr *gfc_simplify_adjustl (gfc_expr *); gfc_expr *gfc_simplify_adjustr (gfc_expr *); gfc_expr *gfc_simplify_aimag (gfc_expr *); gfc_expr *gfc_simplify_aint (gfc_expr *, gfc_expr *); gfc_expr *gfc_simplify_all (gfc_expr *, gfc_expr *); -gfc_expr *gfc_simplify_atrigd (gfc_expr *); +gfc_expr *gfc_simplify_asind (gfc_expr *); gfc_expr *gfc_simplify_dint (gfc_expr *); gfc_expr *gfc_simplify_anint (gfc_expr *, gfc_expr *); gfc_expr *gfc_simplify_dnint (gfc_expr *); @@ -252,6 +253,7 @@ gfc_expr *gfc_simplify_any (gfc_expr *, gfc_expr *); gfc_expr *gfc_simplify_asin (gfc_expr *); gfc_expr *gfc_simplify_asinh (gfc_expr *); gfc_expr *gfc_simplify_atan (gfc_expr *); +gfc_expr *gfc_simplify_atand (gfc_expr *); gfc_expr *gfc_simplify_atanh (gfc_expr *); gfc_expr *gfc_simplify_atan2 (gfc_expr *, gfc_expr *); gfc_expr *gfc_simplify_atan2d (gfc_expr *, gfc_expr *); @@ -277,8 +279,10 @@ gfc_expr *gfc_simplify_compiler_version (void); gfc_expr *gfc_simplify_complex (gfc_expr *, gfc_expr *); gfc_expr *gfc_simplify_conjg (gfc_expr *); gfc_expr *gfc_simplify_cos (gfc_expr *); +gfc_expr *gfc_simplify_cosd (gfc_expr *); gfc_expr *gfc_simplify_cosh (gfc_expr *); gfc_expr *gfc_simplify_cotan (gfc_expr *); +gfc_expr *gfc_simplify_cotand (gfc_expr *); gfc_expr *gfc_simplify_count (gfc_expr *, gfc_expr *, gfc_expr *); gfc_expr *gfc_simplify_cshift (gfc_expr *, gfc_expr *, gfc_expr *); gfc_expr *gfc_simplify_dcmplx (gfc_expr *, gfc_expr *); @@ -404,6 +408,7 @@ gfc_expr *gfc_simplify_shifta (gfc_expr *, gfc_expr *) gfc_expr *gfc_simplify_shiftl (gfc_expr *, gfc_expr *); gfc_expr *gfc_simplify_shiftr (gfc_expr *, gfc_expr *); gfc_expr *gfc_simplify_sin (gfc_expr *); +gfc_expr *gfc_simplify_sind (gfc_expr *); gfc_expr *gfc_simplify_sinh (gfc_expr *); gfc_expr *gfc_simplify_size (gfc_expr *, gfc_expr *, gfc_expr *); gfc_expr *gfc_simplify_sizeof (gfc_expr *); @@ -414,13 +419,13 @@ gfc_expr *gfc_simplify_spread (gfc_expr *, gfc_expr *, gfc_expr *gfc_simplify_sqrt (gfc_expr *); gfc_expr *gfc_simplify_sum (gfc_expr *, gfc_expr *, gfc_expr *); gfc_expr *gfc_simplify_tan (gfc_expr *); +gfc_expr *gfc_simplify_tand (gfc_expr *); gfc_expr *gfc_simplify_tanh (gfc_expr *); gfc_expr *gfc_simplify_this_image (gfc_expr *, gfc_expr *, gfc_expr *); gfc_expr *gfc_simplify_tiny (gfc_expr *); gfc_expr *gfc_simplify_trailz (gfc_expr *); gfc_expr *gfc_simplify_transfer (gfc_expr *, gfc_expr *, gfc_expr *); gfc_expr *gfc_simplify_transpose (gfc_expr *); -gfc_expr *gfc_simplify_trigd (gfc_expr *); gfc_expr *gfc_simplify_trim (gfc_expr *); gfc_expr *gfc_simplify_ubound (gfc_expr *, gfc_expr *, gfc_expr *); gfc_expr *gfc_simplify_ucobound (gfc_expr *, gfc_expr *, gfc_expr *); @@ -473,7 +478,6 @@ void gfc_resolve_conjg (gfc_expr *, gfc_expr *); void gfc_resolve_cos (gfc_expr *, gfc_expr *); void gfc_resolve_cosh (gfc_expr *, gfc_expr *); void gfc_resolve_count (gfc_expr *, gfc_expr *, gfc_expr *, gfc_expr *); -void gfc_resolve_cotan (gfc_expr *, gfc_expr *); void gfc_resolve_cshift (gfc_expr *, gfc_expr *, gfc_expr *, gfc_expr *); void gfc_resolve_ctime (gfc_expr *, gfc_expr *); void gfc_resolve_dble (gfc_expr *, gfc_expr *); @@ -612,7 +616,7 @@ void gfc_resolve_time8 (gfc_expr *); void gfc_resolve_transfer (gfc_expr *, gfc_expr *, gfc_expr *, gfc_expr *); void gfc_resolve_transpose (gfc_expr *, gfc_expr *); void gfc_resolve_trigd (gfc_expr *, gfc_expr *); -void gfc_resolve_atrigd (gfc_expr *, gfc_expr *); +void gfc_resolve_trigd2 (gfc_expr *, gfc_expr *, gfc_expr *); void gfc_resolve_trim (gfc_expr *, gfc_expr *); void gfc_resolve_ttynam (gfc_expr *, gfc_expr *); void gfc_resolve_ubound (gfc_expr *, gfc_expr *, gfc_expr *, gfc_expr *); Index: gcc/fortran/iresolve.c =================================================================== --- gcc/fortran/iresolve.c (revision 280157) +++ gcc/fortran/iresolve.c (working copy) @@ -686,87 +686,7 @@ gfc_resolve_cosh (gfc_expr *f, gfc_expr *x) } -/* Our replacement of elements of a trig call with an EXPR_OP (e.g. - multiplying the result or operands by a factor to convert to/from degrees) - will cause the resolve_* function to be invoked again when resolving the - freshly created EXPR_OP. See gfc_resolve_trigd, gfc_resolve_atrigd, - gfc_resolve_cotan. We must observe this and avoid recursively creating - layers of nested EXPR_OP expressions. */ - -static bool -is_trig_resolved (gfc_expr *f) -{ - /* We know we've already resolved the function if we see the lib call - starting with '__'. */ - return (f->value.function.name != NULL - && gfc_str_startswith (f->value.function.name, "__")); -} - -/* Return a shallow copy of the function expression f. The original expression - has its pointers cleared so that it may be freed without affecting the - shallow copy. This is similar to gfc_copy_expr, but doesn't perform a deep - copy of the argument list, allowing it to be reused somewhere else, - setting the expression up nicely for gfc_replace_expr. */ - -static gfc_expr * -copy_replace_function_shallow (gfc_expr *f) -{ - gfc_expr *fcopy; - gfc_actual_arglist *args; - - /* The only thing deep-copied in gfc_copy_expr is args. */ - args = f->value.function.actual; - f->value.function.actual = NULL; - fcopy = gfc_copy_expr (f); - fcopy->value.function.actual = args; - - /* Clear the old function so the shallow copy is not affected if the old - expression is freed. */ - f->value.function.name = NULL; - f->value.function.isym = NULL; - f->value.function.actual = NULL; - f->value.function.esym = NULL; - f->shape = NULL; - f->ref = NULL; - - return fcopy; -} - - -/* Resolve cotan = cos / sin. */ - void -gfc_resolve_cotan (gfc_expr *f, gfc_expr *x) -{ - gfc_expr *result, *fcopy, *sin; - gfc_actual_arglist *sin_args; - - if (is_trig_resolved (f)) - return; - - /* Compute cotan (x) = cos (x) / sin (x). */ - f->value.function.isym = gfc_intrinsic_function_by_id (GFC_ISYM_COS); - gfc_resolve_cos (f, x); - - sin_args = gfc_get_actual_arglist (); - sin_args->expr = gfc_copy_expr (x); - - sin = gfc_get_expr (); - sin->ts = f->ts; - sin->where = f->where; - sin->expr_type = EXPR_FUNCTION; - sin->value.function.isym = gfc_intrinsic_function_by_id (GFC_ISYM_SIN); - sin->value.function.actual = sin_args; - gfc_resolve_sin (sin, sin_args->expr); - - /* Replace f with cos/sin - we do this in place in f for the caller. */ - fcopy = copy_replace_function_shallow (f); - result = gfc_divide (fcopy, sin); - gfc_replace_expr (f, result); -} - - -void gfc_resolve_count (gfc_expr *f, gfc_expr *mask, gfc_expr *dim, gfc_expr *kind) { f->ts.type = BT_INTEGER; @@ -2909,158 +2829,6 @@ gfc_resolve_tanh (gfc_expr *f, gfc_expr *x) } -/* Build an expression for converting degrees to radians. */ - -static gfc_expr * -get_radians (gfc_expr *deg) -{ - gfc_expr *result, *factor; - gfc_actual_arglist *mod_args; - - gcc_assert (deg->ts.type == BT_REAL); - - /* Set deg = deg % 360 to avoid offsets from large angles. */ - factor = gfc_get_constant_expr (deg->ts.type, deg->ts.kind, °->where); - mpfr_set_d (factor->value.real, 360.0, GFC_RND_MODE); - - mod_args = gfc_get_actual_arglist (); - mod_args->expr = deg; - mod_args->next = gfc_get_actual_arglist (); - mod_args->next->expr = factor; - - result = gfc_get_expr (); - result->ts = deg->ts; - result->where = deg->where; - result->expr_type = EXPR_FUNCTION; - result->value.function.isym = gfc_intrinsic_function_by_id (GFC_ISYM_MOD); - result->value.function.actual = mod_args; - - /* Set factor = pi / 180. */ - factor = gfc_get_constant_expr (deg->ts.type, deg->ts.kind, °->where); - mpfr_const_pi (factor->value.real, GFC_RND_MODE); - mpfr_div_ui (factor->value.real, factor->value.real, 180, GFC_RND_MODE); - - /* Result is rad = (deg % 360) * (pi / 180). */ - result = gfc_multiply (result, factor); - return result; -} - - -/* Build an expression for converting radians to degrees. */ - -static gfc_expr * -get_degrees (gfc_expr *rad) -{ - gfc_expr *result, *factor; - gfc_actual_arglist *mod_args; - mpfr_t tmp; - - gcc_assert (rad->ts.type == BT_REAL); - - /* Set rad = rad % 2pi to avoid offsets from large angles. */ - factor = gfc_get_constant_expr (rad->ts.type, rad->ts.kind, &rad->where); - mpfr_const_pi (factor->value.real, GFC_RND_MODE); - mpfr_mul_ui (factor->value.real, factor->value.real, 2, GFC_RND_MODE); - - mod_args = gfc_get_actual_arglist (); - mod_args->expr = rad; - mod_args->next = gfc_get_actual_arglist (); - mod_args->next->expr = factor; - - result = gfc_get_expr (); - result->ts = rad->ts; - result->where = rad->where; - result->expr_type = EXPR_FUNCTION; - result->value.function.isym = gfc_intrinsic_function_by_id (GFC_ISYM_MOD); - result->value.function.actual = mod_args; - - /* Set factor = 180 / pi. */ - factor = gfc_get_constant_expr (rad->ts.type, rad->ts.kind, &rad->where); - mpfr_set_ui (factor->value.real, 180, GFC_RND_MODE); - mpfr_init (tmp); - mpfr_const_pi (tmp, GFC_RND_MODE); - mpfr_div (factor->value.real, factor->value.real, tmp, GFC_RND_MODE); - mpfr_clear (tmp); - - /* Result is deg = (rad % 2pi) * (180 / pi). */ - result = gfc_multiply (result, factor); - return result; -} - - -/* Resolve a call to a trig function. */ - -static void -resolve_trig_call (gfc_expr *f, gfc_expr *x) -{ - switch (f->value.function.isym->id) - { - case GFC_ISYM_ACOS: - return gfc_resolve_acos (f, x); - case GFC_ISYM_ASIN: - return gfc_resolve_asin (f, x); - case GFC_ISYM_ATAN: - return gfc_resolve_atan (f, x); - case GFC_ISYM_ATAN2: - /* NB. arg3 is unused for atan2 */ - return gfc_resolve_atan2 (f, x, NULL); - case GFC_ISYM_COS: - return gfc_resolve_cos (f, x); - case GFC_ISYM_COTAN: - return gfc_resolve_cotan (f, x); - case GFC_ISYM_SIN: - return gfc_resolve_sin (f, x); - case GFC_ISYM_TAN: - return gfc_resolve_tan (f, x); - default: - gcc_unreachable (); - } -} - -/* Resolve degree trig function as trigd (x) = trig (radians (x)). */ - -void -gfc_resolve_trigd (gfc_expr *f, gfc_expr *x) -{ - if (is_trig_resolved (f)) - return; - - x = get_radians (x); - f->value.function.actual->expr = x; - - resolve_trig_call (f, x); -} - - -/* Resolve degree inverse trig function as atrigd (x) = degrees (atrig (x)). */ - -void -gfc_resolve_atrigd (gfc_expr *f, gfc_expr *x) -{ - gfc_expr *result, *fcopy; - - if (is_trig_resolved (f)) - return; - - resolve_trig_call (f, x); - - fcopy = copy_replace_function_shallow (f); - result = get_degrees (fcopy); - gfc_replace_expr (f, result); -} - - -/* Resolve atan2d(x) = degrees(atan2(x)). */ - -void -gfc_resolve_atan2d (gfc_expr *f, gfc_expr *x, gfc_expr *y ATTRIBUTE_UNUSED) -{ - /* Note that we lose the second arg here - that's okay because it is - unused in gfc_resolve_atan2 anyway. */ - gfc_resolve_atrigd (f, x); -} - - /* Resolve failed_images (team, kind). */ void @@ -3280,7 +3048,7 @@ gfc_resolve_transpose (gfc_expr *f, gfc_expr *matrix) default: f->value.function.name = (matrix->ts.type == BT_CHARACTER ? PREFIX ("transpose_char") - : PREFIX ("transpose")); + : "transpose"); break; } } @@ -3292,6 +3060,30 @@ gfc_resolve_trim (gfc_expr *f, gfc_expr *string) f->ts.type = BT_CHARACTER; f->ts.kind = string->ts.kind; f->value.function.name = gfc_get_string ("__trim_%d", string->ts.kind); +} + + +/* Resolve the degree trignometric functions. This amounts to setting + the function return type-spec from its argument and building a + library function names of the form _gfortran_sind_r4. */ + +void +gfc_resolve_trigd (gfc_expr *f, gfc_expr *x) +{ + f->ts = x->ts; + f->value.function.name + = gfc_get_string (PREFIX ("%s_%c%d"), f->value.function.isym->name, + gfc_type_letter (x->ts.type), x->ts.kind); +} + + +void +gfc_resolve_trigd2 (gfc_expr *f, gfc_expr *y, gfc_expr *x) +{ + f->ts = y->ts; + f->value.function.name + = gfc_get_string (PREFIX ("%s_%d"), f->value.function.isym->name, + x->ts.kind); } Index: gcc/fortran/simplify.c =================================================================== --- gcc/fortran/simplify.c (revision 280157) +++ gcc/fortran/simplify.c (working copy) @@ -1107,7 +1107,92 @@ gfc_simplify_asin (gfc_expr *x) } +/* Convert radians to degrees, i.e., x * 180 / pi. */ + +static void +rad2deg (mpfr_t x) +{ + mpfr_t tmp; + + mpfr_init (tmp); + mpfr_const_pi (tmp, GFC_RND_MODE); + mpfr_mul_ui (x, x, 180, GFC_RND_MODE); + mpfr_div (x, x, tmp, GFC_RND_MODE); + mpfr_clear (tmp); +} + + +/* Simplify ACOSD(X) where the returned value has units of degree. */ + gfc_expr * +gfc_simplify_acosd (gfc_expr *x) +{ + gfc_expr *result; + + if (x->expr_type != EXPR_CONSTANT) + return NULL; + + if (mpfr_cmp_si (x->value.real, 1) > 0 + || mpfr_cmp_si (x->value.real, -1) < 0) + { + gfc_error ("Argument of ACOSD at %L must be between -1 and 1", + &x->where); + return &gfc_bad_expr; + } + + result = gfc_get_constant_expr (x->ts.type, x->ts.kind, &x->where); + mpfr_acos (result->value.real, x->value.real, GFC_RND_MODE); + rad2deg (result->value.real); + + return range_check (result, "ACOSD"); +} + + +/* Simplify asind (x) where the returned value has units of degree. */ + +gfc_expr * +gfc_simplify_asind (gfc_expr *x) +{ + gfc_expr *result; + + if (x->expr_type != EXPR_CONSTANT) + return NULL; + + if (mpfr_cmp_si (x->value.real, 1) > 0 + || mpfr_cmp_si (x->value.real, -1) < 0) + { + gfc_error ("Argument of ASIND at %L must be between -1 and 1", + &x->where); + return &gfc_bad_expr; + } + + result = gfc_get_constant_expr (x->ts.type, x->ts.kind, &x->where); + mpfr_asin (result->value.real, x->value.real, GFC_RND_MODE); + rad2deg (result->value.real); + + return range_check (result, "ASIND"); +} + + +/* Simplify atand (x) where the returned value has units of degree. */ + +gfc_expr * +gfc_simplify_atand (gfc_expr *x) +{ + gfc_expr *result; + + if (x->expr_type != EXPR_CONSTANT) + return NULL; + + result = gfc_get_constant_expr (x->ts.type, x->ts.kind, &x->where); + mpfr_atan (result->value.real, x->value.real, GFC_RND_MODE); + rad2deg (result->value.real); + + return range_check (result, "ATAND"); +} + + +gfc_expr * gfc_simplify_asinh (gfc_expr *x) { gfc_expr *result; @@ -1208,8 +1293,8 @@ gfc_simplify_atan2 (gfc_expr *y, gfc_expr *x) if (mpfr_zero_p (y->value.real) && mpfr_zero_p (x->value.real)) { - gfc_error ("If first argument of ATAN2 %L is zero, then the " - "second argument must not be zero", &x->where); + gfc_error ("If first argument of ATAN2 at %L is zero, then the " + "second argument must not be zero", &y->where); return &gfc_bad_expr; } @@ -1736,177 +1821,127 @@ gfc_simplify_conjg (gfc_expr *e) return range_check (result, "CONJG"); } -/* Return the simplification of the constant expression in icall, or NULL - if the expression is not constant. */ -static gfc_expr * -simplify_trig_call (gfc_expr *icall) +/* Simplify atan2d (x) where the unit is degree. */ + +gfc_expr * +gfc_simplify_atan2d (gfc_expr *y, gfc_expr *x) { - gfc_isym_id func = icall->value.function.isym->id; - gfc_expr *x = icall->value.function.actual->expr; + gfc_expr *result; - /* The actual simplifiers will return NULL for non-constant x. */ - switch (func) + if (x->expr_type != EXPR_CONSTANT || y->expr_type != EXPR_CONSTANT) + return NULL; + + if (mpfr_zero_p (y->value.real) && mpfr_zero_p (x->value.real)) { - case GFC_ISYM_ACOS: - return gfc_simplify_acos (x); - case GFC_ISYM_ASIN: - return gfc_simplify_asin (x); - case GFC_ISYM_ATAN: - return gfc_simplify_atan (x); - case GFC_ISYM_COS: - return gfc_simplify_cos (x); - case GFC_ISYM_COTAN: - return gfc_simplify_cotan (x); - case GFC_ISYM_SIN: - return gfc_simplify_sin (x); - case GFC_ISYM_TAN: - return gfc_simplify_tan (x); - default: - gfc_internal_error ("in simplify_trig_call(): Bad intrinsic"); + gfc_error ("If first argument of ATAN2D at %L is zero, then the " + "second argument must not be zero", &y->where); + return &gfc_bad_expr; } -} -/* Convert a floating-point number from radians to degrees. */ + result = gfc_get_constant_expr (x->ts.type, x->ts.kind, &x->where); + mpfr_atan2 (result->value.real, y->value.real, x->value.real, GFC_RND_MODE); + rad2deg (result->value.real); -static void -degrees_f (mpfr_t x, mpfr_rnd_t rnd_mode) -{ - mpfr_t tmp; - mpfr_init (tmp); - - /* Set x = x % 2pi to avoid offsets with large angles. */ - mpfr_const_pi (tmp, rnd_mode); - mpfr_mul_ui (tmp, tmp, 2, rnd_mode); - mpfr_fmod (tmp, x, tmp, rnd_mode); - - /* Set x = x * 180. */ - mpfr_mul_ui (x, x, 180, rnd_mode); - - /* Set x = x / pi. */ - mpfr_const_pi (tmp, rnd_mode); - mpfr_div (x, x, tmp, rnd_mode); - - mpfr_clear (tmp); + return range_check (result, "ATAN2D"); } -/* Convert a floating-point number from degrees to radians. */ -static void -radians_f (mpfr_t x, mpfr_rnd_t rnd_mode) +gfc_expr * +gfc_simplify_cos (gfc_expr *x) { - mpfr_t tmp; - mpfr_init (tmp); + gfc_expr *result; - /* Set x = x % 360 to avoid offsets with large angles. */ - mpfr_set_ui (tmp, 360, rnd_mode); - mpfr_fmod (tmp, x, tmp, rnd_mode); + if (x->expr_type != EXPR_CONSTANT) + return NULL; - /* Set x = x * pi. */ - mpfr_const_pi (tmp, rnd_mode); - mpfr_mul (x, x, tmp, rnd_mode); + result = gfc_get_constant_expr (x->ts.type, x->ts.kind, &x->where); - /* Set x = x / 180. */ - mpfr_div_ui (x, x, 180, rnd_mode); + switch (x->ts.type) + { + case BT_REAL: + mpfr_cos (result->value.real, x->value.real, GFC_RND_MODE); + break; - mpfr_clear (tmp); -} + case BT_COMPLEX: + gfc_set_model_kind (x->ts.kind); + mpc_cos (result->value.complex, x->value.complex, GFC_MPC_RND_MODE); + break; + default: + gfc_internal_error ("in gfc_simplify_cos(): Bad type"); + } -/* Convert argument to radians before calling a trig function. */ - -gfc_expr * -gfc_simplify_trigd (gfc_expr *icall) -{ - gfc_expr *arg; - - arg = icall->value.function.actual->expr; - - if (arg->ts.type != BT_REAL) - gfc_internal_error ("in gfc_simplify_trigd(): Bad type"); - - if (arg->expr_type == EXPR_CONSTANT) - /* Convert constant to radians before passing off to simplifier. */ - radians_f (arg->value.real, GFC_RND_MODE); - - /* Let the usual simplifier take over - we just simplified the arg. */ - return simplify_trig_call (icall); + return range_check (result, "COS"); } -/* Convert result of an inverse trig function to degrees. */ +/* Simplify COSD(X) where X has the unit of degree. */ + gfc_expr * -gfc_simplify_atrigd (gfc_expr *icall) +gfc_simplify_cosd (gfc_expr *x) { + mpfr_t deg2rad; gfc_expr *result; - if (icall->value.function.actual->expr->ts.type != BT_REAL) - gfc_internal_error ("in gfc_simplify_atrigd(): Bad type"); + if (x->expr_type != EXPR_CONSTANT) + return NULL; - /* See if another simplifier has work to do first. */ - result = simplify_trig_call (icall); + mpfr_init (deg2rad); + mpfr_const_pi (deg2rad, GFC_RND_MODE); + mpfr_div_ui (deg2rad, deg2rad, 180, GFC_RND_MODE); + mpfr_mul (deg2rad, x->value.real, deg2rad, GFC_RND_MODE); + result = gfc_get_constant_expr (x->ts.type, x->ts.kind, &x->where); + mpfr_cos (result->value.real, deg2rad, GFC_RND_MODE); + mpfr_clear (deg2rad); - if (result && result->expr_type == EXPR_CONSTANT) - { - /* Convert constant to degrees after passing off to actual simplifier. */ - degrees_f (result->value.real, GFC_RND_MODE); - return result; - } - - /* Let gfc_resolve_atrigd take care of the non-constant case. */ - return NULL; + return range_check (result, "COSD"); } -/* Convert the result of atan2 to degrees. */ +/* Simplify SIND(X) where X has the unit of degree. */ + gfc_expr * -gfc_simplify_atan2d (gfc_expr *y, gfc_expr *x) +gfc_simplify_sind (gfc_expr *x) { + mpfr_t deg2rad; gfc_expr *result; - if (x->ts.type != BT_REAL || y->ts.type != BT_REAL) - gfc_internal_error ("in gfc_simplify_atan2d(): Bad type"); + if (x->expr_type != EXPR_CONSTANT) + return NULL; - if (x->expr_type == EXPR_CONSTANT && y->expr_type == EXPR_CONSTANT) - { - result = gfc_simplify_atan2 (y, x); - if (result != NULL) - { - degrees_f (result->value.real, GFC_RND_MODE); - return result; - } - } + mpfr_init (deg2rad); + mpfr_const_pi (deg2rad, GFC_RND_MODE); + mpfr_div_ui (deg2rad, deg2rad, 180, GFC_RND_MODE); + mpfr_mul (deg2rad, x->value.real, deg2rad, GFC_RND_MODE); + result = gfc_get_constant_expr (x->ts.type, x->ts.kind, &x->where); + mpfr_sin (result->value.real, deg2rad, GFC_RND_MODE); + mpfr_clear (deg2rad); - /* Let gfc_resolve_atan2d take care of the non-constant case. */ - return NULL; + return range_check (result, "SIND"); } + +/* Simplify TAND(X) where X has the unit of degree. */ + gfc_expr * -gfc_simplify_cos (gfc_expr *x) +gfc_simplify_tand (gfc_expr *x) { + mpfr_t deg2rad; gfc_expr *result; if (x->expr_type != EXPR_CONSTANT) return NULL; + mpfr_init (deg2rad); + mpfr_const_pi (deg2rad, GFC_RND_MODE); + mpfr_div_ui (deg2rad, deg2rad, 180, GFC_RND_MODE); + mpfr_mul (deg2rad, x->value.real, deg2rad, GFC_RND_MODE); result = gfc_get_constant_expr (x->ts.type, x->ts.kind, &x->where); + mpfr_tan (result->value.real, deg2rad, GFC_RND_MODE); + mpfr_clear (deg2rad); - switch (x->ts.type) - { - case BT_REAL: - mpfr_cos (result->value.real, x->value.real, GFC_RND_MODE); - break; - - case BT_COMPLEX: - gfc_set_model_kind (x->ts.kind); - mpc_cos (result->value.complex, x->value.complex, GFC_MPC_RND_MODE); - break; - - default: - gfc_internal_error ("in gfc_simplify_cos(): Bad type"); - } - - return range_check (result, "COS"); + return range_check (result, "TAND"); } @@ -7779,6 +7814,8 @@ gfc_simplify_sum (gfc_expr *array, gfc_expr *dim, gfc_ } +/* Simplify COTAN(X) where X has the unit of radian. */ + gfc_expr * gfc_simplify_cotan (gfc_expr *x) { @@ -7811,6 +7848,29 @@ gfc_simplify_cotan (gfc_expr *x) } return range_check (result, "COTAN"); +} + + +/* Simplify COTAND(X) where X has the unit of degree. */ + +gfc_expr * +gfc_simplify_cotand (gfc_expr *x) +{ + mpfr_t deg2rad; + gfc_expr *result; + + if (x->expr_type != EXPR_CONSTANT) + return NULL; + + mpfr_init (deg2rad); + mpfr_const_pi (deg2rad, GFC_RND_MODE); + mpfr_div_ui (deg2rad, deg2rad, 180, GFC_RND_MODE); + mpfr_mul (deg2rad, x->value.real, deg2rad, GFC_RND_MODE); + result = gfc_get_constant_expr (x->ts.type, x->ts.kind, &x->where); + mpfr_cot (result->value.real, deg2rad, GFC_RND_MODE); + mpfr_clear (deg2rad); + + return range_check (result, "COTAND"); } Index: gcc/fortran/trans-intrinsic.c =================================================================== --- gcc/fortran/trans-intrinsic.c (revision 280157) +++ gcc/fortran/trans-intrinsic.c (working copy) @@ -120,6 +120,9 @@ static GTY(()) gfc_intrinsic_map_t gfc_intrinsic_map[] /* Functions in libgfortran. */ LIB_FUNCTION (ERFC_SCALED, "erfc_scaled", false), + LIB_FUNCTION (SIND, "sind", false), + LIB_FUNCTION (COSD, "cosd", false), + LIB_FUNCTION (TAND, "tand", false), /* End the list. */ LIB_FUNCTION (NONE, NULL, false) @@ -4385,6 +4388,183 @@ gfc_conv_intrinsic_anyall (gfc_se * se, gfc_expr * exp se->expr = resvar; } + +/* Generate the constant 180 / pi, which is used in the conversion + of acosd(), asind(), atand(), atan2d(). */ + +static tree +rad2deg (int kind) +{ + tree retval; + mpfr_t pi, t0; + + gfc_set_model_kind (kind); + mpfr_init (pi); + mpfr_init (t0); + mpfr_set_si (t0, 180, GFC_RND_MODE); + mpfr_const_pi (pi, GFC_RND_MODE); + mpfr_div (t0, t0, pi, GFC_RND_MODE); + retval = gfc_conv_mpfr_to_tree (t0, kind, 0); + mpfr_clear (t0); + mpfr_clear (pi); + return retval; +} + + +/* ACOSD(x) is translated into ACOS(x) * 180 / pi. + ASIND(x) is translated into ASIN(x) * 180 / pi. + ATAND(x) is translated into ATAN(x) * 180 / pi. */ + +static void +gfc_conv_intrinsic_atrigd (gfc_se * se, gfc_expr * expr, gfc_isym_id id) +{ + tree arg; + tree atrigd; + tree type; + + type = gfc_typenode_for_spec (&expr->ts); + + gfc_conv_intrinsic_function_args (se, expr, &arg, 1); + + if (id == GFC_ISYM_ACOSD) + atrigd = gfc_builtin_decl_for_float_kind (BUILT_IN_ACOS, expr->ts.kind); + else if (id == GFC_ISYM_ASIND) + atrigd = gfc_builtin_decl_for_float_kind (BUILT_IN_ASIN, expr->ts.kind); + else if (id == GFC_ISYM_ATAND) + atrigd = gfc_builtin_decl_for_float_kind (BUILT_IN_ATAN, expr->ts.kind); + else + gcc_unreachable (); + + atrigd = build_call_expr_loc (input_location, atrigd, 1, arg); + + se->expr = fold_build2_loc (input_location, MULT_EXPR, type, atrigd, + fold_convert (type, rad2deg (expr->ts.kind))); +} + + +/* COTAN(X) is translated into -TAN(X+PI/2) for REAL argument and + COS(X) / SIN(X) for COMPLEX argument. */ + +static void +gfc_conv_intrinsic_cotan (gfc_se *se, gfc_expr *expr) +{ + gfc_intrinsic_map_t *m; + tree arg; + tree type; + + type = gfc_typenode_for_spec (&expr->ts); + gfc_conv_intrinsic_function_args (se, expr, &arg, 1); + + if (expr->ts.type == BT_REAL) + { + tree tan; + tree tmp; + mpfr_t pio2; + + /* Create pi/2. */ + gfc_set_model_kind (expr->ts.kind); + mpfr_init (pio2); + mpfr_const_pi (pio2, GFC_RND_MODE); + mpfr_div_ui (pio2, pio2, 2, GFC_RND_MODE); + tmp = gfc_conv_mpfr_to_tree (pio2, expr->ts.kind, 0); + mpfr_clear (pio2); + + /* Find tan builtin function. */ + m = gfc_intrinsic_map; + for (; m->id != GFC_ISYM_NONE || m->double_built_in != END_BUILTINS; m++) + if (GFC_ISYM_TAN == m->id) + break; + + tmp = fold_build2_loc (input_location, PLUS_EXPR, type, arg, tmp); + tan = gfc_get_intrinsic_lib_fndecl (m, expr); + tan = build_call_expr_loc (input_location, tan, 1, tmp); + se->expr = fold_build1_loc (input_location, NEGATE_EXPR, type, tan); + } + else + { + tree sin; + tree cos; + + /* Find cos builtin function. */ + m = gfc_intrinsic_map; + for (; m->id != GFC_ISYM_NONE || m->double_built_in != END_BUILTINS; m++) + if (GFC_ISYM_COS == m->id) + break; + + cos = gfc_get_intrinsic_lib_fndecl (m, expr); + cos = build_call_expr_loc (input_location, cos, 1, arg); + + /* Find sin builtin function. */ + m = gfc_intrinsic_map; + for (; m->id != GFC_ISYM_NONE || m->double_built_in != END_BUILTINS; m++) + if (GFC_ISYM_SIN == m->id) + break; + + sin = gfc_get_intrinsic_lib_fndecl (m, expr); + sin = build_call_expr_loc (input_location, sin, 1, arg); + + /* Divide cos by sin. */ + se->expr = fold_build2_loc (input_location, RDIV_EXPR, type, cos, sin); + } +} + + +/* COTAND(X) is translated into -TAN(X*PI/180+PI/2) for REAL argument. */ + +static void +gfc_conv_intrinsic_cotand (gfc_se *se, gfc_expr *expr) +{ + tree arg; + tree tmp; + tree type; + mpfr_t pi; + + type = gfc_typenode_for_spec (&expr->ts); + gfc_conv_intrinsic_function_args (se, expr, &arg, 1); + + /* Build the pi/180 tree and multiply by x. */ + gfc_set_model_kind (expr->ts.kind); + mpfr_init (pi); + mpfr_const_pi (pi, GFC_RND_MODE); + mpfr_div_ui (pi, pi, 180, GFC_RND_MODE); + tmp = gfc_conv_mpfr_to_tree (pi, expr->ts.kind, 0); + arg = fold_build2_loc (input_location, MULT_EXPR, type, arg, tmp); + + /* Build the pi/2 tree and add it to arg. */ + mpfr_init (pi); + mpfr_const_pi (pi, GFC_RND_MODE); + mpfr_div_ui (pi, pi, 2, GFC_RND_MODE); + tmp = gfc_conv_mpfr_to_tree (pi, expr->ts.kind, 0); + arg = fold_build2_loc (input_location, PLUS_EXPR, type, arg, tmp); + mpfr_clear (pi); + + tmp = gfc_builtin_decl_for_float_kind (BUILT_IN_TAN, expr->ts.kind); + tmp = build_call_expr_loc (input_location, tmp, 1, arg); + + se->expr = fold_build1_loc (input_location, NEGATE_EXPR, type, tmp); +} + + +/* ATAN2D(Y,X) is translated into ATAN2(Y,X) * 180 / PI. */ + +static void +gfc_conv_intrinsic_atan2d (gfc_se *se, gfc_expr *expr) +{ + tree args[2]; + tree atan2d; + tree type; + + gfc_conv_intrinsic_function_args (se, expr, args, 2); + type = TREE_TYPE (args[0]); + + atan2d = gfc_builtin_decl_for_float_kind (BUILT_IN_ATAN2, expr->ts.kind); + atan2d = build_call_expr_loc (input_location, atan2d, 2, args[0], args[1]); + + se->expr = fold_build2_loc (input_location, MULT_EXPR, type, atan2d, + rad2deg (expr->ts.kind)); +} + + /* COUNT(A) = Number of true elements in A. */ static void gfc_conv_intrinsic_count (gfc_se * se, gfc_expr * expr) @@ -9896,6 +10076,24 @@ gfc_conv_intrinsic_function (gfc_se * se, gfc_expr * e case GFC_ISYM_ANY: gfc_conv_intrinsic_anyall (se, expr, NE_EXPR); + break; + + case GFC_ISYM_ACOSD: + case GFC_ISYM_ASIND: + case GFC_ISYM_ATAND: + gfc_conv_intrinsic_atrigd (se, expr, expr->value.function.isym->id); + break; + + case GFC_ISYM_COTAN: + gfc_conv_intrinsic_cotan (se, expr); + break; + + case GFC_ISYM_COTAND: + gfc_conv_intrinsic_cotand (se, expr); + break; + + case GFC_ISYM_ATAN2D: + gfc_conv_intrinsic_atan2d (se, expr); break; case GFC_ISYM_BTEST: Index: libgfortran/Makefile.am =================================================================== --- libgfortran/Makefile.am (revision 280157) +++ libgfortran/Makefile.am (working copy) @@ -141,6 +141,7 @@ intrinsics/reshape_generic.c \ intrinsics/reshape_packed.c \ intrinsics/selected_int_kind.f90 \ intrinsics/selected_real_kind.f90 \ +intrinsics/trigd.c \ intrinsics/unpack_generic.c \ runtime/in_pack_generic.c \ runtime/in_unpack_generic.c Index: libgfortran/Makefile.in =================================================================== --- libgfortran/Makefile.in (revision 280157) +++ libgfortran/Makefile.in (working copy) @@ -421,8 +421,9 @@ am__objects_58 = associated.lo abort.lo args.lo cshift pack_generic.lo selected_char_kind.lo size.lo \ spread_generic.lo string_intrinsics.lo rand.lo random.lo \ reshape_generic.lo reshape_packed.lo selected_int_kind.lo \ - selected_real_kind.lo unpack_generic.lo in_pack_generic.lo \ - in_unpack_generic.lo $(am__objects_56) $(am__objects_57) + selected_real_kind.lo trigd.lo unpack_generic.lo \ + in_pack_generic.lo in_unpack_generic.lo $(am__objects_56) \ + $(am__objects_57) @IEEE_SUPPORT_TRUE@am__objects_59 = ieee_arithmetic.lo \ @IEEE_SUPPORT_TRUE@ ieee_exceptions.lo ieee_features.lo am__objects_60 = @@ -770,9 +771,9 @@ gfor_helper_src = intrinsics/associated.c intrinsics/a intrinsics/rand.c intrinsics/random.c \ intrinsics/reshape_generic.c intrinsics/reshape_packed.c \ intrinsics/selected_int_kind.f90 \ - intrinsics/selected_real_kind.f90 intrinsics/unpack_generic.c \ - runtime/in_pack_generic.c runtime/in_unpack_generic.c \ - $(am__append_3) $(am__append_4) + intrinsics/selected_real_kind.f90 intrinsics/trigd.c \ + intrinsics/unpack_generic.c runtime/in_pack_generic.c \ + runtime/in_unpack_generic.c $(am__append_3) $(am__append_4) @IEEE_SUPPORT_FALSE@gfor_ieee_src = @IEEE_SUPPORT_TRUE@gfor_ieee_src = \ @IEEE_SUPPORT_TRUE@ieee/ieee_arithmetic.F90 \ @@ -2251,6 +2252,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/time.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/transfer.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/transfer128.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/trigd.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/umask.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unit.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unix.Plo@am__quote@ @@ -6402,6 +6404,13 @@ reshape_packed.lo: intrinsics/reshape_packed.c @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='intrinsics/reshape_packed.c' object='reshape_packed.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o reshape_packed.lo `test -f 'intrinsics/reshape_packed.c' || echo '$(srcdir)/'`intrinsics/reshape_packed.c + +trigd.lo: intrinsics/trigd.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT trigd.lo -MD -MP -MF $(DEPDIR)/trigd.Tpo -c -o trigd.lo `test -f 'intrinsics/trigd.c' || echo '$(srcdir)/'`intrinsics/trigd.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/trigd.Tpo $(DEPDIR)/trigd.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='intrinsics/trigd.c' object='trigd.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o trigd.lo `test -f 'intrinsics/trigd.c' || echo '$(srcdir)/'`intrinsics/trigd.c unpack_generic.lo: intrinsics/unpack_generic.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT unpack_generic.lo -MD -MP -MF $(DEPDIR)/unpack_generic.Tpo -c -o unpack_generic.lo `test -f 'intrinsics/unpack_generic.c' || echo '$(srcdir)/'`intrinsics/unpack_generic.c Index: libgfortran/gfortran.map =================================================================== --- libgfortran/gfortran.map (revision 280157) +++ libgfortran/gfortran.map (working copy) @@ -1606,4 +1606,16 @@ GFORTRAN_9.2 { GFORTRAN_10 { global: _gfortran_os_error_at; + _gfortran_sind_r4; + _gfortran_sind_r8; + _gfortran_sind_r10; + _gfortran_sind_r16; + _gfortran_cosd_r4; + _gfortran_cosd_r8; + _gfortran_cosd_r10; + _gfortran_cosd_r16; + _gfortran_tand_r4; + _gfortran_tand_r8; + _gfortran_tand_r10; + _gfortran_tand_r16; } GFORTRAN_9.2; Index: libgfortran/intrinsics/trigd.c =================================================================== --- libgfortran/intrinsics/trigd.c (nonexistent) +++ libgfortran/intrinsics/trigd.c (working copy) @@ -0,0 +1,229 @@ +/* Implementation of the degree trignometric functions COSD, SIND, TAND. + Copyright (C) 2020 Free Software Foundation, Inc. + Contributed by Steven G. Kargl <ka...@gcc.gnu.org> + +This file is part of the GNU Fortran runtime library (libgfortran). + +Libgfortran is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public +License as published by the Free Software Foundation; either +version 3 of the License, or (at your option) any later version. + +Libgfortran is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +<http://www.gnu.org/licenses/>. */ + +#include "libgfortran.h" + +#include <math.h> + +/* Build _gfortran_sind_r4, _gfortran_cosd_r4, and _gfortran_tand_r4 */ + +#define WTYPE GFC_REAL_4 +#define SIND sind_r4 +#define COSD cosd_r4 +#define TAND tand_r4 + +#define SMALL 0x1.p-7f +#define HALF 0.5f +#define COS30 8.66025388e-01f +#define SIN60 8.66025388e-01f + +#define PIO180H 1.74560547e-02f /* high 12 bits. */ +#define PIO180L -2.76216747e-06f /* Next 24 bits. */ +#define PIO180 (PIO180L + PIO180H) /* pi / 180. */ +#define D2R(x) (fmaf((x), PIO180H, (x) * PIO180L)) +#define CPYSGN(x) copysignf(1.f,(x)) +#define FABS(x) fabsf((x)) +#define FMOD(x) fmodf((x), 360.f) +#define SIN(x) sinf((x)) +#define COS(x) cosf((x)) +#define TAN(x) tanf((x)) + +#include "trigd_inc.c" + +#undef WTYPE +#undef SMALL +#undef HALF +#undef COS30 +#undef SIN60 +#undef PIO180H +#undef PIO180L +#undef PIO180 +#undef D2R +#undef CPYSGN +#undef FABS +#undef FMOD +#undef SIN +#undef COS +#undef TAN +#undef SIND +#undef COSD +#undef TAND + + +/* Build _gfortran_sind_r8, _gfortran_cosd_r8, and _gfortran_tand_r8. */ + +#define WTYPE GFC_REAL_8 +#define SIND sind_r8 +#define COSD cosd_r8 +#define TAND tand_r8 + +#define SMALL 0x1.p-21 +#define HALF 0.5 +#define COS30 8.6602540378443860e-01 +#define SIN60 8.6602540378443860e-01 + +#define PIO180H 1.7453283071517944e-02 /* high 21 bits. */ +#define PIO180L 9.4484253514332993e-09 /* Next 53 bits. */ +#define PIO180 (PIO180L + PIO180H) /* pi / 180. */ +#define D2R(x) (fma((x), PIO180H, (x) * PIO180L)) + +#define CPYSGN(x) copysign(1.,(x)) +#define FABS(x) fabs((x)) +#define FMOD(x) fmod((x), 360.) +#define SIN(x) sin((x)) +#define COS(x) cos((x)) +#define TAN(x) tan((x)) + +#include "trigd_inc.c" + +#undef WTYPE +#undef SMALL +#undef HALF +#undef COS30 +#undef SIN60 +#undef PIO180H +#undef PIO180L +#undef PIO180 +#undef D2R +#undef CPYSGN +#undef FABS +#undef FMOD +#undef SIN +#undef COS +#undef TAN +#undef SIND +#undef COSD +#undef TAND + + +/* Build _gfortran_sind_r10, _gfortran_cosd_r10, and _gfortran_tand_r10. */ + +#ifdef HAVE_GFC_REAL_10 + +#define WTYPE GFC_REAL_10 +#define SIND sind_r10 +#define COSD cosd_r10 +#define TAND tand_r10 + +#define SMALL 0x1.p-27L +#define HALF 0.5L +#define COS30 8.66025403784438646787e-01L +#define SIN60 8.66025403784438646787e-01L + +#define PIO180H 1.74532925229868851602e-02L /* high 32 bits */ +#define PIO180L -3.04358939097084072823e-12L /* Next 64 bits */ +#define PIO180 (PIO180L + PIO180H) /* pi / 180 */ +#define D2R(x) (fmal((x), PIO180H, (x) * PIO180L)) +#define CPYSGN(x) copysignl(1.L,(x)) +#define FABS(x) fabsl((x)) +#define FMOD(x) fmodl((x), 360.L) +#define SIN(x) sinl((x)) +#define COS(x) cosl((x)) +#define TAN(x) tanl((x)) + +#include "trigd_inc.c" +#undef WTYPE +#undef SMALL +#undef HALF +#undef COS30 +#undef SIN60 +#undef PIO180H +#undef PIO180L +#undef PIO180 +#undef D2R +#undef CPYSGN +#undef FABS +#undef FMOD +#undef SIN +#undef COS +#undef TAN +#undef SIND +#undef COSD +#undef TAND +#endif + + +/* Build _gfortran_sind_r16, _gfortran_cosd_r16, and _gfortran_tand_r16. */ + +#ifdef HAVE_GFC_REAL_16 + +#define WTYPE GFC_REAL_16 +#define SIND sind_r16 +#define COSD cosd_r16 +#define TAND tand_r16 + +#ifdef GFC_REAL_16_IS_FLOAT128 /* libquadmath. */ +#define SMALL 3.4e-17Q +#define HALF 0.5Q +#define COS30 8.66025403784438646763723170752936183e-01Q +#define SIN60 8.66025403784438646763723170752936183e-01Q +#define PIO180H 1.74532925199433197605003442731685936e-02Q +#define PIO180L -2.39912634365882824665106671063098954e-17Q +#define PIO180 (PIO180L + PIO180H) +#define D2R(x) (fmaq((x), PIO180H, (x) * PIO180L)) +#define CPYSGN(x) copysignq(1.Q,(x)) +#define FABS(x) fabsq((x)) +#define FMOD(x) fmodq((x), 360.Q) +#define SIN(x) sinq((x)) +#define COS(x) cosq((x)) +#define TAN(x) tanq((x)) +#else +#define SMALL 3.4e-17L +#define HALF 0.5L +#define COS30 8.66025403784438646763723170752936183e-01L +#define SIN60 8.66025403784438646763723170752936183e-01L +#define PIO180H 1.74532925199433197605003442731685936e-02L +#define PIO180L -2.39912634365882824665106671063098954e-17L +#define PIO180 (PIO180L + PIO180H) +#define D2R(x) (fmal((x), PIO180H, (x) * PIO180L)) +#define CPYSGN(x) copysignl(1.L,(x)) +#define FABS(x) fabsl((x)) +#define FMOD(x) fmodl((x), 360.L) +#define SIN(x) sinl((x)) +#define COS(x) cosl((x)) +#define TAN(x) tanl((x)) +#endif + +#include "trigd_inc.c" + +#undef WTYPE +#undef SMALL +#undef HALF +#undef COS30 +#undef SIN60 +#undef PIO180H +#undef PIO180L +#undef PIO180 +#undef D2R +#undef CPYSGN +#undef FABS +#undef FMOD +#undef SIN +#undef COS +#undef TAN +#undef SIND +#undef COSD +#undef TAND +#endif Index: libgfortran/intrinsics/trigd_inc.c =================================================================== --- libgfortran/intrinsics/trigd_inc.c (nonexistent) +++ libgfortran/intrinsics/trigd_inc.c (working copy) @@ -0,0 +1,202 @@ +/* Implementation of the degree trignometric functions COSD, SIND, TAND. + Copyright (C) 2020 Free Software Foundation, Inc. + Contributed by Steven G. Kargl <ka...@gcc.gnu.org> + +This file is part of the GNU Fortran runtime library (libgfortran). + +Libgfortran is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public +License as published by the Free Software Foundation; either +version 3 of the License, or (at your option) any later version. + +Libgfortran is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +<http://www.gnu.org/licenses/>. */ + + +/* Compute sind(x) = sin(x * pi / 180). */ + +extern WTYPE SIND (WTYPE *); +export_proto(SIND); + +extern WTYPE COSD (WTYPE *); +export_proto(COSD); + +extern WTYPE TAND (WTYPE *); +export_proto(TAND); + +WTYPE +SIND (WTYPE *x) +{ + int n; + WTYPE ax, s; + + if (isfinite(*x)) + { + /* sin(-x) = - sin(x). */ + s = CPYSGN(*x); + ax = FABS(*x); + + /* In radians, sin(x) = x as x -> 0. */ + if (ax < SMALL) + return (*x * PIO180); + + /* Reduce angle to ax in [0,360]. */ + ax = FMOD(ax); + + /* Special cases. */ + n = (int)ax; + if (n%30 == 0 && ax == n) + { + if (n%180 == 0) + ax = 0; + else if (n%90 == 0) + ax = n == 90 ? 1 : -1; + else if (n%60 == 0) + ax = n < 180 ? SIN60 : -SIN60; + else + ax = n < 180 ? HALF : -HALF; + return (s * ax); + } + + /* Fold [0,360] into the range [0,45], and compute either SIN() or + COS() depending on symmetry of shifting into the [0,45] range. */ + if (ax <= 180) + { + if (ax <= 90) + ax = (ax <= 45) ? SIN(D2R(ax)) : COS(D2R(90 - ax)); + else + ax = (ax <= 135) ? COS(D2R(ax - 90)) : SIN(D2R(180 - ax)); + } + else if (ax <= 270) + ax = (ax <= 225) ? -SIN(D2R(ax - 180)) : -COS(D2R(270 - ax)); + else + ax = (ax <= 315) ? -COS(D2R(ax - 270)) : -SIN(D2R(360 - ax)); + ax *= s; + } + else + ax = x - x; /* Return NaN for +-Inf and NaN and raise exception. */ + + return (ax); +} + +/* Compute cosd(x) = cos(x * pi / 180). */ + +WTYPE +COSD (WTYPE *x) +{ +#ifdef TINYF + static const volatile WTYPE tiny = 1.e-30f; +#elif TINY + static const volatile WTYPE tiny = 1.e-300f; +#else + static const volatile WTYPE tiny = 0x1p-10000L; +#endif + + int n; + WTYPE ax; + + if (isfinite(*x)) + { + ax = FABS(*x); + + /* No spurious underflows!. In radians, cos(x) = 1-x*x/2 as x -> 0. */ + if (ax < SMALL) + return (ax == 0 ? 1 : 1 - tiny); + + /* Reduce angle to ax in [0,360]. */ + ax = FMOD(ax); + + /* Special cases with exact results. */ + n = (int)ax; + if (n%30 == 0 && ax == n) + { + if (n%180 == 0) + ax = n == 270 ? -1 : 1; + else if (n%90 == 0) + ax = 0; + else if (n%60 == 0) + ax = (n == 60 || n == 300) ? 0.5f : -0.5f; + else + ax = (n == 30 || n == 330) ? COS30 : -COS30; + return (ax); + } + + /* Fold [0,360] into the range [0,45], and compute either SIN() or + COS() depending on symmetry of shifting into the [0,45] range. */ + if (ax <= 180) + { + if (ax <= 90) + ax = (ax <= 45) ? COS(D2R(ax)) : SIN(D2R(90 - ax)); + else + ax = (ax <= 135) ? -SIN(D2R(ax - 90)) : -COS(D2R(180 - ax)); + } + else if (ax <= 270) + ax = (ax <= 225) ? -COS(D2R(ax - 180)) : -SIN(D2R(270 - ax)); + else + ax = (ax <= 315) ? SIN(D2R(ax - 270)) : COS(D2R(360 - ax)); + } + else + ax = x - x; /* Return NaN for +-Inf and NaN and raise exception. */ + + return (ax); +} + + +/* Compute tand(x) = tan(x * pi / 180). */ + +WTYPE +TAND (WTYPE *x) +{ + int n; + WTYPE ax, s; + + if (isfinite(*x)) + { + /* tan(-x) = - tan(x). */ + s = CPYSGN(*x); + ax = FABS(*x); + + /* In radians, tan(x) = x as x -> 0. */ + if (ax < SMALL) + return (*x * PIO180); + + ax = FMOD(ax); + + /* Special cases with exact results. */ + n = (int)ax; + if (n%45 == 0 && ax == n) + { + if (n%180 == 0) + ax = 0; + else if (n%90 == 0) + ax = (x - x) / (x - x); + else + ax = (n == 45 || n == 225) ? 1 : -1; + return (s * ax); + } + + /* Fold [0,360] into the range [0,90], and compute TAN(). */ + if (ax <= 180) + ax = (ax <= 90) ? TAN(D2R(ax)) : -TAN(D2R(180 - ax)); + else + ax = (ax <= 270) ? TAN(D2R(ax - 180)) : -TAN(D2R(360 - ax)); + + ax *= s; + } + else + ax = x - x; /* Return NaN for +-Inf and NaN and raise exception. */ + + return (ax); +} +