diff options
author | Fritz Reese <fritzoreese@gmail.com> | 2016-10-11 11:21:07 +0000 |
---|---|---|
committer | Fritz Reese <foreese@gcc.gnu.org> | 2016-10-11 11:21:07 +0000 |
commit | 8e8c2744faa0cfa9697229b074b951e70bf50e1b (patch) | |
tree | d575169173ea76fc3df30eb1dd5be2ec0a60ee4c /gcc/fortran/iresolve.c | |
parent | 9760fbe005693d949db626b0a2cc6a6d3801b8ba (diff) | |
download | gcc-8e8c2744faa0cfa9697229b074b951e70bf50e1b.zip gcc-8e8c2744faa0cfa9697229b074b951e70bf50e1b.tar.gz gcc-8e8c2744faa0cfa9697229b074b951e70bf50e1b.tar.bz2 |
New flag -fdec-math for COTAN and degree trig intrinsics.
2016-10-11 Fritz Reese <fritzoreese@gmail.com>
New flag -fdec-math for COTAN and degree trig intrinsics.
gcc/fortran/
* lang.opt: New flag -fdec-math.
* options.c (set_dec_flags): Enable with -fdec.
* invoke.texi, gfortran.texi, intrinsic.texi: Update documentation.
* intrinsics.c (add_functions, do_simplify): New intrinsics
with -fdec-math.
* gfortran.h (gfc_isym_id): New isym GFC_ISYM_COTAN.
* gfortran.h (gfc_resolve_atan2d, gfc_resolve_cotan,
gfc_resolve_trigd, gfc_resolve_atrigd): New prototypes.
* iresolve.c (resolve_trig_call, get_degrees, get_radians,
is_trig_resolved, gfc_resolve_cotan, gfc_resolve_trigd,
gfc_resolve_atrigd, gfc_resolve_atan2d): New functions.
* intrinsics.h (gfc_simplify_atan2d, gfc_simplify_atrigd,
gfc_simplify_cotan, gfc_simplify_trigd): New prototypes.
* simplify.c (simplify_trig_call, degrees_f, radians_f,
gfc_simplify_cotan, gfc_simplify_trigd, gfc_simplify_atrigd,
gfc_simplify_atan2d): New functions.
gcc/testsuite/gfortran.dg/
* dec_math.f90: New testsuite.
From-SVN: r240989
Diffstat (limited to 'gcc/fortran/iresolve.c')
-rw-r--r-- | gcc/fortran/iresolve.c | 233 |
1 files changed, 233 insertions, 0 deletions
diff --git a/gcc/fortran/iresolve.c b/gcc/fortran/iresolve.c index ecea1c3..f4f81b2 100644 --- a/gcc/fortran/iresolve.c +++ b/gcc/fortran/iresolve.c @@ -673,6 +673,86 @@ 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 + && 0 == strncmp ("__", f->value.function.name, 2); +} + +/* 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) { @@ -2578,6 +2658,159 @@ 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_d (factor->value.real, factor->value.real, 180.0, 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; + + 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_d (factor->value.real, 180.0, 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: + break; + } + + 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); +} + + void gfc_resolve_image_index (gfc_expr *f, gfc_expr *array ATTRIBUTE_UNUSED, gfc_expr *sub ATTRIBUTE_UNUSED) |