diff options
Diffstat (limited to 'gcc/fortran/trans-intrinsic.c')
-rw-r--r-- | gcc/fortran/trans-intrinsic.c | 3003 |
1 files changed, 3003 insertions, 0 deletions
diff --git a/gcc/fortran/trans-intrinsic.c b/gcc/fortran/trans-intrinsic.c new file mode 100644 index 00000000000..fb3ceb2f6b1 --- /dev/null +++ b/gcc/fortran/trans-intrinsic.c @@ -0,0 +1,3003 @@ +/* Intrinsic translation + Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc. + Contributed by Paul Brook <paul@nowt.org> + and Steven Bosscher <s.bosscher@student.tudelft.nl> + +This file is part of GNU G95. + +GNU G95 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 2, or (at your option) +any later version. + +GNU G95 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. + +You should have received a copy of the GNU General Public License +along with GNU G95; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* trans-intrinsic.c-- generate GENERIC trees for calls to intrinsics. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tree.h" +#include <stdio.h> +#include <string.h> +#include "ggc.h" +#include "toplev.h" +#include "real.h" +#include "tree-simple.h" +#include "flags.h" +#include <gmp.h> +#include <assert.h> +#include "gfortran.h" +#include "intrinsic.h" +#include "trans.h" +#include "trans-const.h" +#include "trans-types.h" +#include "trans-array.h" +#include "defaults.h" +/* Only for gfc_trans_assign and gfc_trans_pointer_assign. */ +#include "trans-stmt.h" + +/* This maps fortran intrinsic math functions to external library or GCC + builtin functions. */ +typedef struct gfc_intrinsic_map_t GTY(()) +{ + /* The explicit enum is required to work around inadequacies in the + garbage collection/gengtype parsing mechanism. */ + enum gfc_generic_isym_id id; + + /* Enum value from the "language-independent", aka C-centric, part + of gcc, or END_BUILTINS of no such value set. */ + /* ??? There are now complex variants in builtins.def, though we + don't currently do anything with them. */ + enum built_in_function code4; + enum built_in_function code8; + + /* True if the naming pattern is to prepend "c" for complex and + append "f" for kind=4. False if the naming pattern is to + prepend "_gfortran_" and append "[rc][48]". */ + bool libm_name; + + /* True if a complex version of the function exists. */ + bool complex_available; + + /* True if the function should be marked const. */ + bool is_constant; + + /* The base library name of this function. */ + const char *name; + + /* Cache decls created for the various operand types. */ + tree real4_decl; + tree real8_decl; + tree complex4_decl; + tree complex8_decl; +} +gfc_intrinsic_map_t; + +/* ??? The NARGS==1 hack here is based on the fact that (c99 at least) + defines complex variants of all of the entries in mathbuiltins.def + except for atan2. */ +#define DEFINE_MATH_BUILTIN(ID, NAME, NARGS) \ + { GFC_ISYM_ ## ID, BUILT_IN_ ## ID ## F, BUILT_IN_ ## ID, true, \ + NARGS == 1, true, NAME, NULL_TREE, NULL_TREE, NULL_TREE, NULL_TREE}, + +#define LIBM_FUNCTION(ID, NAME, HAVE_COMPLEX) \ + { GFC_ISYM_ ## ID, END_BUILTINS, END_BUILTINS, true, HAVE_COMPLEX, true, \ + NAME, NULL_TREE, NULL_TREE, NULL_TREE, NULL_TREE } + +#define LIBF_FUNCTION(ID, NAME, HAVE_COMPLEX) \ + { GFC_ISYM_ ## ID, END_BUILTINS, END_BUILTINS, false, HAVE_COMPLEX, true, \ + NAME, NULL_TREE, NULL_TREE, NULL_TREE, NULL_TREE } + +static GTY(()) gfc_intrinsic_map_t gfc_intrinsic_map[] = +{ + /* Functions built into gcc itself. */ +#include "mathbuiltins.def" + + /* Functions in libm. */ + /* ??? This does exist as BUILT_IN_SCALBN, but doesn't quite fit the + pattern for other mathbuiltins.def entries. At present we have no + optimizations for this in the common sources. */ + LIBM_FUNCTION (SCALE, "scalbn", false), + + /* Functions in libgfortran. */ + LIBF_FUNCTION (FRACTION, "fraction", false), + LIBF_FUNCTION (NEAREST, "nearest", false), + LIBF_FUNCTION (SET_EXPONENT, "set_exponent", false), + + /* End the list. */ + LIBF_FUNCTION (NONE, NULL, false) +}; +#undef DEFINE_MATH_BUILTIN +#undef LIBM_FUNCTION +#undef LIBF_FUNCTION + +/* Structure for storing components of a floating number to be used by + elemental functions to manipulate reals. */ +typedef struct +{ + tree arg; /* Variable tree to view convert to integer. */ + tree expn; /* Variable tree to save exponent. */ + tree frac; /* Variable tree to save fraction. */ + tree smask; /* Constant tree of sign's mask. */ + tree emask; /* Constant tree of exponent's mask. */ + tree fmask; /* Constant tree of fraction's mask. */ + tree edigits; /* Constant tree of bit numbers of exponent. */ + tree fdigits; /* Constant tree of bit numbers of fraction. */ + tree f1; /* Constant tree of the f1 defined in the real model. */ + tree bias; /* Constant tree of the bias of exponent in the memory. */ + tree type; /* Type tree of arg1. */ + tree mtype; /* Type tree of integer type. Kind is that of arg1. */ +} +real_compnt_info; + + +/* Evaluate the arguments to an intrinsic function. */ + +static tree +gfc_conv_intrinsic_function_args (gfc_se * se, gfc_expr * expr) +{ + gfc_actual_arglist *actual; + tree args; + gfc_se argse; + + args = NULL_TREE; + for (actual = expr->value.function.actual; actual; actual = actual->next) + { + /* Skip ommitted optional arguments. */ + if (!actual->expr) + continue; + + /* Evaluate the parameter. This will substitute scalarized + references automatically. */ + gfc_init_se (&argse, se); + + if (actual->expr->ts.type == BT_CHARACTER) + { + gfc_conv_expr (&argse, actual->expr); + gfc_conv_string_parameter (&argse); + args = gfc_chainon_list (args, argse.string_length); + } + else + gfc_conv_expr_val (&argse, actual->expr); + + gfc_add_block_to_block (&se->pre, &argse.pre); + gfc_add_block_to_block (&se->post, &argse.post); + args = gfc_chainon_list (args, argse.expr); + } + return args; +} + + +/* Conversions between different types are output by the frontend as + intrinsic functions. We implement these directly with inline code. */ + +static void +gfc_conv_intrinsic_conversion (gfc_se * se, gfc_expr * expr) +{ + tree type; + tree arg; + + /* Evaluate the argument. */ + type = gfc_typenode_for_spec (&expr->ts); + assert (expr->value.function.actual->expr); + arg = gfc_conv_intrinsic_function_args (se, expr); + arg = TREE_VALUE (arg); + + /* Conversion from complex to non-complex involves taking the real + component of the value. */ + if (TREE_CODE (TREE_TYPE (arg)) == COMPLEX_TYPE + && expr->ts.type != BT_COMPLEX) + { + tree artype; + + artype = TREE_TYPE (TREE_TYPE (arg)); + arg = build1 (REALPART_EXPR, artype, arg); + } + + se->expr = convert (type, arg); +} + + +/* This is needed because the gcc backend only implements FIX_TRUNC_EXPR + TRUNC(x) = INT(x) <= x ? INT(x) : INT(x) - 1 + Similarly for CEILING. */ + +static tree +build_fixbound_expr (stmtblock_t * pblock, tree arg, tree type, int up) +{ + tree tmp; + tree cond; + tree argtype; + tree intval; + + argtype = TREE_TYPE (arg); + arg = gfc_evaluate_now (arg, pblock); + + intval = convert (type, arg); + intval = gfc_evaluate_now (intval, pblock); + + tmp = convert (argtype, intval); + cond = build (up ? GE_EXPR : LE_EXPR, boolean_type_node, tmp, arg); + + tmp = build (up ? PLUS_EXPR : MINUS_EXPR, type, intval, integer_one_node); + tmp = build (COND_EXPR, type, cond, intval, tmp); + return tmp; +} + + +/* This is needed because the gcc backend only implements FIX_TRUNC_EXPR + NINT(x) = INT(x + ((x > 0) ? 0.5 : -0.5)). */ + +static tree +build_round_expr (stmtblock_t * pblock, tree arg, tree type) +{ + tree tmp; + tree cond; + tree neg; + tree pos; + tree argtype; + REAL_VALUE_TYPE r; + + argtype = TREE_TYPE (arg); + arg = gfc_evaluate_now (arg, pblock); + + real_from_string (&r, "0.5"); + pos = build_real (argtype, r); + + real_from_string (&r, "-0.5"); + neg = build_real (argtype, r); + + tmp = gfc_build_const (argtype, integer_zero_node); + cond = fold (build (GT_EXPR, boolean_type_node, arg, tmp)); + + tmp = fold (build (COND_EXPR, argtype, cond, pos, neg)); + tmp = fold (build (PLUS_EXPR, argtype, arg, tmp)); + return fold (build1 (FIX_TRUNC_EXPR, type, tmp)); +} + + +/* Convert a real to an integer using a specific rounding mode. + Ideally we would just build the corresponding GENERIC node, + however the RTL expander only actually supports FIX_TRUNC_EXPR. */ + +static tree +build_fix_expr (stmtblock_t * pblock, tree arg, tree type, int op) +{ + switch (op) + { + case FIX_FLOOR_EXPR: + return build_fixbound_expr (pblock, arg, type, 0); + break; + + case FIX_CEIL_EXPR: + return build_fixbound_expr (pblock, arg, type, 1); + break; + + case FIX_ROUND_EXPR: + return build_round_expr (pblock, arg, type); + + default: + return build1 (op, type, arg); + } +} + + +/* Round a real value using the specified rounding mode. + We use a temporary integer of that same kind size as the result. + Values larger than can be represented by this kind are unchanged, as + will not be accurate enough to represent the rounding. + huge = HUGE (KIND (a)) + aint (a) = ((a > huge) || (a < -huge)) ? a : (real)(int)a + */ + +static void +gfc_conv_intrinsic_aint (gfc_se * se, gfc_expr * expr, int op) +{ + tree type; + tree itype; + tree arg; + tree tmp; + tree cond; + mpf_t huge; + int n; + int kind; + + kind = expr->ts.kind; + + n = END_BUILTINS; + /* We have builtin functions for some cases. */ + switch (op) + { + case FIX_ROUND_EXPR: + switch (kind) + { + case 4: + n = BUILT_IN_ROUNDF; + break; + + case 8: + n = BUILT_IN_ROUND; + break; + } + break; + + case FIX_FLOOR_EXPR: + switch (kind) + { + case 4: + n = BUILT_IN_FLOORF; + break; + + case 8: + n = BUILT_IN_FLOOR; + break; + } + } + + /* Evaluate the argument. */ + assert (expr->value.function.actual->expr); + arg = gfc_conv_intrinsic_function_args (se, expr); + + /* Use a builtin function if one exists. */ + if (n != END_BUILTINS) + { + tmp = built_in_decls[n]; + se->expr = gfc_build_function_call (tmp, arg); + return; + } + + /* This code is probably redundant, but we'll keep it lying around just + in case. */ + type = gfc_typenode_for_spec (&expr->ts); + arg = TREE_VALUE (arg); + arg = gfc_evaluate_now (arg, &se->pre); + + /* Test if the value is too large to handle sensibly. */ + mpf_init (huge); + n = gfc_validate_kind (BT_INTEGER, kind); + mpf_set_z (huge, gfc_integer_kinds[n].huge); + tmp = gfc_conv_mpf_to_tree (huge, kind); + cond = build (LT_EXPR, boolean_type_node, arg, tmp); + + mpf_neg (huge, huge); + tmp = gfc_conv_mpf_to_tree (huge, kind); + tmp = build (GT_EXPR, boolean_type_node, arg, tmp); + cond = build (TRUTH_AND_EXPR, boolean_type_node, cond, tmp); + itype = gfc_get_int_type (kind); + + tmp = build_fix_expr (&se->pre, arg, itype, op); + tmp = convert (type, tmp); + se->expr = build (COND_EXPR, type, cond, tmp, arg); +} + + +/* Convert to an integer using the specified rounding mode. */ + +static void +gfc_conv_intrinsic_int (gfc_se * se, gfc_expr * expr, int op) +{ + tree type; + tree arg; + + /* Evaluate the argument. */ + type = gfc_typenode_for_spec (&expr->ts); + assert (expr->value.function.actual->expr); + arg = gfc_conv_intrinsic_function_args (se, expr); + arg = TREE_VALUE (arg); + + if (TREE_CODE (TREE_TYPE (arg)) == INTEGER_TYPE) + { + /* Conversion to a different integer kind. */ + se->expr = convert (type, arg); + } + else + { + /* Conversion from complex to non-complex involves taking the real + component of the value. */ + if (TREE_CODE (TREE_TYPE (arg)) == COMPLEX_TYPE + && expr->ts.type != BT_COMPLEX) + { + tree artype; + + artype = TREE_TYPE (TREE_TYPE (arg)); + arg = build1 (REALPART_EXPR, artype, arg); + } + + se->expr = build_fix_expr (&se->pre, arg, type, op); + } +} + + +/* Get the imaginary component of a value. */ + +static void +gfc_conv_intrinsic_imagpart (gfc_se * se, gfc_expr * expr) +{ + tree arg; + + arg = gfc_conv_intrinsic_function_args (se, expr); + arg = TREE_VALUE (arg); + se->expr = build1 (IMAGPART_EXPR, TREE_TYPE (TREE_TYPE (arg)), arg); +} + + +/* Get the complex conjugate of a value. */ + +static void +gfc_conv_intrinsic_conjg (gfc_se * se, gfc_expr * expr) +{ + tree arg; + + arg = gfc_conv_intrinsic_function_args (se, expr); + arg = TREE_VALUE (arg); + se->expr = build1 (CONJ_EXPR, TREE_TYPE (arg), arg); +} + + +/* Initialize function decls for library functions. The external functions + are created as required. Builtin functions are added here. */ + +void +gfc_build_intrinsic_lib_fndecls (void) +{ + gfc_intrinsic_map_t *m; + + /* Add GCC builtin functions. */ + for (m = gfc_intrinsic_map; m->id != GFC_ISYM_NONE; m++) + { + if (m->code4 != END_BUILTINS) + m->real4_decl = built_in_decls[m->code4]; + if (m->code8 != END_BUILTINS) + m->real8_decl = built_in_decls[m->code8]; + } +} + + +/* Create a fndecl for a simple intrinsic library function. */ + +static tree +gfc_get_intrinsic_lib_fndecl (gfc_intrinsic_map_t * m, gfc_expr * expr) +{ + tree type; + tree argtypes; + tree fndecl; + gfc_actual_arglist *actual; + tree *pdecl; + gfc_typespec *ts; + char name[GFC_MAX_SYMBOL_LEN + 3]; + + ts = &expr->ts; + if (ts->type == BT_REAL) + { + switch (ts->kind) + { + case 4: + pdecl = &m->real4_decl; + break; + case 8: + pdecl = &m->real8_decl; + break; + default: + abort (); + } + } + else if (ts->type == BT_COMPLEX) + { + if (!m->complex_available) + abort (); + + switch (ts->kind) + { + case 4: + pdecl = &m->complex4_decl; + break; + case 8: + pdecl = &m->complex8_decl; + break; + default: + abort (); + } + } + else + abort (); + + if (*pdecl) + return *pdecl; + + if (m->libm_name) + { + if (ts->kind != 4 && ts->kind != 8) + abort (); + snprintf (name, sizeof (name), "%s%s%s", + ts->type == BT_COMPLEX ? "c" : "", + m->name, + ts->kind == 4 ? "f" : ""); + } + else + { + snprintf (name, sizeof (name), PREFIX ("%s_%c%d"), m->name, + ts->type == BT_COMPLEX ? 'c' : 'r', + ts->kind); + } + + argtypes = NULL_TREE; + for (actual = expr->value.function.actual; actual; actual = actual->next) + { + type = gfc_typenode_for_spec (&actual->expr->ts); + argtypes = gfc_chainon_list (argtypes, type); + } + argtypes = gfc_chainon_list (argtypes, void_type_node); + type = build_function_type (gfc_typenode_for_spec (ts), argtypes); + fndecl = build_decl (FUNCTION_DECL, get_identifier (name), type); + + /* Mark the decl as external. */ + DECL_EXTERNAL (fndecl) = 1; + TREE_PUBLIC (fndecl) = 1; + + /* Mark it __attribute__((const)), if possible. */ + TREE_READONLY (fndecl) = m->is_constant; + + rest_of_decl_compilation (fndecl, NULL, 1, 0); + + (*pdecl) = fndecl; + return fndecl; +} + + +/* Convert an intrinsic function into an external or builtin call. */ + +static void +gfc_conv_intrinsic_lib_function (gfc_se * se, gfc_expr * expr) +{ + gfc_intrinsic_map_t *m; + tree args; + tree fndecl; + gfc_generic_isym_id id; + + id = expr->value.function.isym->generic_id; + /* Find the entry for this function. */ + for (m = gfc_intrinsic_map; m->id != GFC_ISYM_NONE; m++) + { + if (id == m->id) + break; + } + + if (m->id == GFC_ISYM_NONE) + { + internal_error ("Intrinsic function %s(%d) not recognized", + expr->value.function.name, id); + } + + /* Get the decl and generate the call. */ + args = gfc_conv_intrinsic_function_args (se, expr); + fndecl = gfc_get_intrinsic_lib_fndecl (m, expr); + se->expr = gfc_build_function_call (fndecl, args); +} + +/* Generate code for EXPONENT(X) intrinsic function. */ + +static void +gfc_conv_intrinsic_exponent (gfc_se * se, gfc_expr * expr) +{ + tree args, fndecl; + gfc_expr *a1; + + args = gfc_conv_intrinsic_function_args (se, expr); + + a1 = expr->value.function.actual->expr; + switch (a1->ts.kind) + { + case 4: + fndecl = gfor_fndecl_math_exponent4; + break; + case 8: + fndecl = gfor_fndecl_math_exponent8; + break; + default: + abort (); + } + + se->expr = gfc_build_function_call (fndecl, args); +} + +/* Evaluate a single upper or lower bound. */ +/* TODO: bound intrinsic generates way too much unneccessary code. */ + +static void +gfc_conv_intrinsic_bound (gfc_se * se, gfc_expr * expr, int upper) +{ + gfc_actual_arglist *arg; + gfc_actual_arglist *arg2; + tree desc; + tree type; + tree bound; + tree tmp; + tree cond; + gfc_se argse; + gfc_ss *ss; + int i; + + gfc_init_se (&argse, NULL); + arg = expr->value.function.actual; + arg2 = arg->next; + + if (se->ss) + { + /* Create an implicit second parameter from the loop variable. */ + assert (!arg2->expr); + assert (se->loop->dimen == 1); + assert (se->ss->expr == expr); + gfc_advance_se_ss_chain (se); + bound = se->loop->loopvar[0]; + bound = fold (build (MINUS_EXPR, gfc_array_index_type, bound, + se->loop->from[0])); + } + else + { + /* use the passed argument. */ + assert (arg->next->expr); + gfc_init_se (&argse, NULL); + gfc_conv_expr_type (&argse, arg->next->expr, gfc_array_index_type); + gfc_add_block_to_block (&se->pre, &argse.pre); + bound = argse.expr; + /* Convert from one based to zero based. */ + bound = fold (build (MINUS_EXPR, gfc_array_index_type, bound, + integer_one_node)); + } + + /* TODO: don't re-evaluate the descriptor on each iteration. */ + /* Get a descriptor for the first parameter. */ + ss = gfc_walk_expr (arg->expr); + assert (ss != gfc_ss_terminator); + argse.want_pointer = 0; + gfc_conv_expr_descriptor (&argse, arg->expr, ss); + gfc_add_block_to_block (&se->pre, &argse.pre); + gfc_add_block_to_block (&se->post, &argse.post); + + desc = argse.expr; + + if (INTEGER_CST_P (bound)) + { + assert (TREE_INT_CST_HIGH (bound) == 0); + i = TREE_INT_CST_LOW (bound); + assert (i >= 0 && i < GFC_TYPE_ARRAY_RANK (TREE_TYPE (desc))); + } + else + { + if (flag_bounds_check) + { + bound = gfc_evaluate_now (bound, &se->pre); + cond = fold (build (LT_EXPR, boolean_type_node, bound, + integer_zero_node)); + tmp = gfc_rank_cst[GFC_TYPE_ARRAY_RANK (TREE_TYPE (desc))]; + tmp = fold (build (GE_EXPR, boolean_type_node, bound, tmp)); + cond = fold(build (TRUTH_ORIF_EXPR, boolean_type_node, cond, tmp)); + gfc_trans_runtime_check (cond, gfc_strconst_fault, &se->pre); + } + } + + if (upper) + se->expr = gfc_conv_descriptor_ubound(desc, bound); + else + se->expr = gfc_conv_descriptor_lbound(desc, bound); + + type = gfc_typenode_for_spec (&expr->ts); + se->expr = convert (type, se->expr); +} + + +static void +gfc_conv_intrinsic_abs (gfc_se * se, gfc_expr * expr) +{ + tree args; + tree val; + tree fndecl; + + args = gfc_conv_intrinsic_function_args (se, expr); + assert (args && TREE_CHAIN (args) == NULL_TREE); + val = TREE_VALUE (args); + + switch (expr->value.function.actual->expr->ts.type) + { + case BT_INTEGER: + case BT_REAL: + se->expr = build1 (ABS_EXPR, TREE_TYPE (val), val); + break; + + case BT_COMPLEX: + switch (expr->ts.kind) + { + case 4: + fndecl = gfor_fndecl_math_cabsf; + break; + case 8: + fndecl = gfor_fndecl_math_cabs; + break; + default: + abort (); + } + se->expr = gfc_build_function_call (fndecl, args); + break; + + default: + abort (); + } +} + + +/* Create a complex value from one or two real components. */ + +static void +gfc_conv_intrinsic_cmplx (gfc_se * se, gfc_expr * expr, int both) +{ + tree arg; + tree real; + tree imag; + tree type; + + type = gfc_typenode_for_spec (&expr->ts); + arg = gfc_conv_intrinsic_function_args (se, expr); + real = convert (TREE_TYPE (type), TREE_VALUE (arg)); + if (both) + imag = convert (TREE_TYPE (type), TREE_VALUE (TREE_CHAIN (arg))); + else if (TREE_CODE (TREE_TYPE (TREE_VALUE (arg))) == COMPLEX_TYPE) + { + arg = TREE_VALUE (arg); + imag = build1 (IMAGPART_EXPR, TREE_TYPE (TREE_TYPE (arg)), arg); + imag = convert (TREE_TYPE (type), imag); + } + else + imag = build_real_from_int_cst (TREE_TYPE (type), integer_zero_node); + + se->expr = fold (build (COMPLEX_EXPR, type, real, imag)); +} + +/* Remainder function MOD(A, P) = A - INT(A / P) * P. + MODULO(A, P) = (A==0 .or. !(A>0 .xor. P>0))? MOD(A,P):MOD(A,P)+P. */ +/* TODO: MOD(x, 0) */ + +static void +gfc_conv_intrinsic_mod (gfc_se * se, gfc_expr * expr, int modulo) +{ + tree arg; + tree arg2; + tree type; + tree itype; + tree tmp; + tree zero; + tree test; + tree test2; + mpf_t huge; + int n; + + arg = gfc_conv_intrinsic_function_args (se, expr); + arg2 = TREE_VALUE (TREE_CHAIN (arg)); + arg = TREE_VALUE (arg); + type = TREE_TYPE (arg); + + switch (expr->ts.type) + { + case BT_INTEGER: + /* Integer case is easy, we've got a builtin op. */ + se->expr = build (TRUNC_MOD_EXPR, type, arg, arg2); + break; + + case BT_REAL: + /* Real values we have to do the hard way. */ + arg = gfc_evaluate_now (arg, &se->pre); + arg2 = gfc_evaluate_now (arg2, &se->pre); + + tmp = build (RDIV_EXPR, type, arg, arg2); + /* Test if the value is too large to handle sensibly. */ + mpf_init (huge); + n = gfc_validate_kind (BT_INTEGER, expr->ts.kind); + mpf_set_z (huge, gfc_integer_kinds[n].huge); + test = gfc_conv_mpf_to_tree (huge, expr->ts.kind); + test2 = build (LT_EXPR, boolean_type_node, tmp, test); + + mpf_neg (huge, huge); + test = gfc_conv_mpf_to_tree (huge, expr->ts.kind); + test = build (GT_EXPR, boolean_type_node, tmp, test); + test2 = build (TRUTH_AND_EXPR, boolean_type_node, test, test2); + + itype = gfc_get_int_type (expr->ts.kind); + tmp = build_fix_expr (&se->pre, tmp, itype, FIX_TRUNC_EXPR); + tmp = convert (type, tmp); + tmp = build (COND_EXPR, type, test2, tmp, arg); + tmp = build (MULT_EXPR, type, tmp, arg2); + se->expr = build (MINUS_EXPR, type, arg, tmp); + break; + + default: + abort (); + } + + if (modulo) + { + zero = gfc_build_const (type, integer_zero_node); + /* Build !(A > 0 .xor. P > 0). */ + test = build (GT_EXPR, boolean_type_node, arg, zero); + test2 = build (GT_EXPR, boolean_type_node, arg2, zero); + test = build (TRUTH_XOR_EXPR, boolean_type_node, test, test2); + test = build1 (TRUTH_NOT_EXPR, boolean_type_node, test); + /* Build (A == 0) .or. !(A > 0 .xor. P > 0). */ + test2 = build (EQ_EXPR, boolean_type_node, arg, zero); + test = build (TRUTH_OR_EXPR, boolean_type_node, test, test2); + + se->expr = build (COND_EXPR, type, test, se->expr, + build (PLUS_EXPR, type, se->expr, arg2)); + } +} + +/* Positive difference DIM (x, y) = ((x - y) < 0) ? 0 : x - y. */ + +static void +gfc_conv_intrinsic_dim (gfc_se * se, gfc_expr * expr) +{ + tree arg; + tree arg2; + tree val; + tree tmp; + tree type; + tree zero; + + arg = gfc_conv_intrinsic_function_args (se, expr); + arg2 = TREE_VALUE (TREE_CHAIN (arg)); + arg = TREE_VALUE (arg); + type = TREE_TYPE (arg); + + val = build (MINUS_EXPR, type, arg, arg2); + val = gfc_evaluate_now (val, &se->pre); + + zero = gfc_build_const (type, integer_zero_node); + tmp = build (LE_EXPR, boolean_type_node, val, zero); + se->expr = build (COND_EXPR, type, tmp, zero, val); +} + + +/* SIGN(A, B) is absolute value of A times sign of B. + The real value versions use library functions to ensure the correct + handling of negative zero. Integer case implemented as: + SIGN(A, B) = ((a >= 0) .xor. (b >= 0)) ? a : -a + */ + +static void +gfc_conv_intrinsic_sign (gfc_se * se, gfc_expr * expr) +{ + tree tmp; + tree arg; + tree arg2; + tree type; + tree zero; + tree testa; + tree testb; + + + arg = gfc_conv_intrinsic_function_args (se, expr); + if (expr->ts.type == BT_REAL) + { + switch (expr->ts.kind) + { + case 4: + tmp = gfor_fndecl_math_sign4; + break; + case 8: + tmp = gfor_fndecl_math_sign8; + break; + default: + abort (); + } + se->expr = gfc_build_function_call (tmp, arg); + return; + } + + arg2 = TREE_VALUE (TREE_CHAIN (arg)); + arg = TREE_VALUE (arg); + type = TREE_TYPE (arg); + zero = gfc_build_const (type, integer_zero_node); + + testa = fold (build (GE_EXPR, boolean_type_node, arg, zero)); + testb = fold (build (GE_EXPR, boolean_type_node, arg2, zero)); + tmp = fold (build (TRUTH_XOR_EXPR, boolean_type_node, testa, testb)); + se->expr = fold (build (COND_EXPR, type, tmp, + build1 (NEGATE_EXPR, type, arg), arg)); +} + + +/* Test for the presence of an optional argument. */ + +static void +gfc_conv_intrinsic_present (gfc_se * se, gfc_expr * expr) +{ + gfc_expr *arg; + + arg = expr->value.function.actual->expr; + assert (arg->expr_type == EXPR_VARIABLE); + se->expr = gfc_conv_expr_present (arg->symtree->n.sym); + se->expr = convert (gfc_typenode_for_spec (&expr->ts), se->expr); +} + + +/* Calculate the double precision product of two single precision values. */ + +static void +gfc_conv_intrinsic_dprod (gfc_se * se, gfc_expr * expr) +{ + tree arg; + tree arg2; + tree type; + + arg = gfc_conv_intrinsic_function_args (se, expr); + arg2 = TREE_VALUE (TREE_CHAIN (arg)); + arg = TREE_VALUE (arg); + + /* Convert the args to double precision before multiplying. */ + type = gfc_typenode_for_spec (&expr->ts); + arg = convert (type, arg); + arg2 = convert (type, arg2); + se->expr = build (MULT_EXPR, type, arg, arg2); +} + + +/* Return a length one character string containing an ascii character. */ + +static void +gfc_conv_intrinsic_char (gfc_se * se, gfc_expr * expr) +{ + tree arg; + tree var; + tree type; + + arg = gfc_conv_intrinsic_function_args (se, expr); + arg = TREE_VALUE (arg); + + /* We currently don't support character types != 1. */ + assert (expr->ts.kind == 1); + type = gfc_character1_type_node; + var = gfc_create_var (type, "char"); + + arg = convert (type, arg); + gfc_add_modify_expr (&se->pre, var, arg); + se->expr = gfc_build_addr_expr (build_pointer_type (type), var); + se->string_length = integer_one_node; +} + + +/* Get the minimum/maximum value of all the parameters. + minmax (a1, a2, a3, ...) + { + if (a2 .op. a1) + mvar = a2; + else + mvar = a1; + if (a3 .op. mvar) + mvar = a3; + ... + return mvar + } + */ + +/* TODO: Mismatching types can occur when specific names are used. + These should be handled during resolution. */ +static void +gfc_conv_intrinsic_minmax (gfc_se * se, gfc_expr * expr, int op) +{ + tree limit; + tree tmp; + tree mvar; + tree val; + tree thencase; + tree elsecase; + tree arg; + tree type; + + arg = gfc_conv_intrinsic_function_args (se, expr); + type = gfc_typenode_for_spec (&expr->ts); + + limit = TREE_VALUE (arg); + if (TREE_TYPE (limit) != type) + limit = convert (type, limit); + /* Only evaluate the argument once. */ + if (TREE_CODE (limit) != VAR_DECL && !TREE_CONSTANT (limit)) + limit = gfc_evaluate_now(limit, &se->pre); + + mvar = gfc_create_var (type, "M"); + elsecase = build_v (MODIFY_EXPR, mvar, limit); + for (arg = TREE_CHAIN (arg); arg != NULL_TREE; arg = TREE_CHAIN (arg)) + { + val = TREE_VALUE (arg); + if (TREE_TYPE (val) != type) + val = convert (type, val); + + /* Only evaluate the argument once. */ + if (TREE_CODE (val) != VAR_DECL && !TREE_CONSTANT (val)) + val = gfc_evaluate_now(val, &se->pre); + + thencase = build_v (MODIFY_EXPR, mvar, convert (type, val)); + + tmp = build (op, boolean_type_node, val, limit); + tmp = build_v (COND_EXPR, tmp, thencase, elsecase); + gfc_add_expr_to_block (&se->pre, tmp); + elsecase = build_empty_stmt (); + limit = mvar; + } + se->expr = mvar; +} + + +/* Create a symbol node for this intrinsic. The symbol form the frontend + is for the generic name. */ + +static gfc_symbol * +gfc_get_symbol_for_expr (gfc_expr * expr) +{ + gfc_symbol *sym; + + /* TODO: Add symbols for intrinsic function to the global namespace. */ + assert (strlen (expr->value.function.name) <= GFC_MAX_SYMBOL_LEN - 5); + sym = gfc_new_symbol (expr->value.function.name, NULL); + + sym->ts = expr->ts; + sym->attr.external = 1; + sym->attr.function = 1; + sym->attr.always_explicit = 1; + sym->attr.proc = PROC_INTRINSIC; + sym->attr.flavor = FL_PROCEDURE; + sym->result = sym; + if (expr->rank > 0) + { + sym->attr.dimension = 1; + sym->as = gfc_get_array_spec (); + sym->as->type = AS_ASSUMED_SHAPE; + sym->as->rank = expr->rank; + } + + /* TODO: proper argument lists for external intrinsics. */ + return sym; +} + +/* Generate a call to an external intrinsic function. */ +static void +gfc_conv_intrinsic_funcall (gfc_se * se, gfc_expr * expr) +{ + gfc_symbol *sym; + + assert (!se->ss || se->ss->expr == expr); + + if (se->ss) + assert (expr->rank > 0); + else + assert (expr->rank == 0); + + sym = gfc_get_symbol_for_expr (expr); + gfc_conv_function_call (se, sym, expr->value.function.actual); + gfc_free (sym); +} + +/* ANY and ALL intrinsics. ANY->op == NE_EXPR, ALL->op == EQ_EXPR. + Implemented as + any(a) + { + forall (i=...) + if (a[i] != 0) + return 1 + end forall + return 0 + } + all(a) + { + forall (i=...) + if (a[i] == 0) + return 0 + end forall + return 1 + } + */ +static void +gfc_conv_intrinsic_anyall (gfc_se * se, gfc_expr * expr, int op) +{ + tree resvar; + stmtblock_t block; + stmtblock_t body; + tree type; + tree tmp; + tree found; + gfc_loopinfo loop; + gfc_actual_arglist *actual; + gfc_ss *arrayss; + gfc_se arrayse; + tree exit_label; + + if (se->ss) + { + gfc_conv_intrinsic_funcall (se, expr); + return; + } + + actual = expr->value.function.actual; + type = gfc_typenode_for_spec (&expr->ts); + /* Initialize the result. */ + resvar = gfc_create_var (type, "test"); + if (op == EQ_EXPR) + tmp = convert (type, boolean_true_node); + else + tmp = convert (type, boolean_false_node); + gfc_add_modify_expr (&se->pre, resvar, tmp); + + /* Walk the arguments. */ + arrayss = gfc_walk_expr (actual->expr); + assert (arrayss != gfc_ss_terminator); + + /* Initialize the scalarizer. */ + gfc_init_loopinfo (&loop); + exit_label = gfc_build_label_decl (NULL_TREE); + TREE_USED (exit_label) = 1; + gfc_add_ss_to_loop (&loop, arrayss); + + /* Initialize the loop. */ + gfc_conv_ss_startstride (&loop); + gfc_conv_loop_setup (&loop); + + gfc_mark_ss_chain_used (arrayss, 1); + /* Generate the loop body. */ + gfc_start_scalarized_body (&loop, &body); + + /* If the condition matches then set the return value. */ + gfc_start_block (&block); + if (op == EQ_EXPR) + tmp = convert (type, boolean_false_node); + else + tmp = convert (type, boolean_true_node); + gfc_add_modify_expr (&block, resvar, tmp); + + /* And break out of the loop. */ + tmp = build1_v (GOTO_EXPR, exit_label); + gfc_add_expr_to_block (&block, tmp); + + found = gfc_finish_block (&block); + + /* Check this element. */ + gfc_init_se (&arrayse, NULL); + gfc_copy_loopinfo_to_se (&arrayse, &loop); + arrayse.ss = arrayss; + gfc_conv_expr_val (&arrayse, actual->expr); + + gfc_add_block_to_block (&body, &arrayse.pre); + tmp = build (op, boolean_type_node, arrayse.expr, integer_zero_node); + tmp = build_v (COND_EXPR, tmp, found, build_empty_stmt ()); + gfc_add_expr_to_block (&body, tmp); + gfc_add_block_to_block (&body, &arrayse.post); + + gfc_trans_scalarizing_loops (&loop, &body); + + /* Add the exit label. */ + tmp = build1_v (LABEL_EXPR, exit_label); + gfc_add_expr_to_block (&loop.pre, tmp); + + gfc_add_block_to_block (&se->pre, &loop.pre); + gfc_add_block_to_block (&se->pre, &loop.post); + gfc_cleanup_loop (&loop); + + se->expr = resvar; +} + +/* COUNT(A) = Number of true elements in A. */ +static void +gfc_conv_intrinsic_count (gfc_se * se, gfc_expr * expr) +{ + tree resvar; + tree type; + stmtblock_t body; + tree tmp; + gfc_loopinfo loop; + gfc_actual_arglist *actual; + gfc_ss *arrayss; + gfc_se arrayse; + + if (se->ss) + { + gfc_conv_intrinsic_funcall (se, expr); + return; + } + + actual = expr->value.function.actual; + + type = gfc_typenode_for_spec (&expr->ts); + /* Initialize the result. */ + resvar = gfc_create_var (type, "count"); + gfc_add_modify_expr (&se->pre, resvar, integer_zero_node); + + /* Walk the arguments. */ + arrayss = gfc_walk_expr (actual->expr); + assert (arrayss != gfc_ss_terminator); + + /* Initialize the scalarizer. */ + gfc_init_loopinfo (&loop); + gfc_add_ss_to_loop (&loop, arrayss); + + /* Initialize the loop. */ + gfc_conv_ss_startstride (&loop); + gfc_conv_loop_setup (&loop); + + gfc_mark_ss_chain_used (arrayss, 1); + /* Generate the loop body. */ + gfc_start_scalarized_body (&loop, &body); + + tmp = build (PLUS_EXPR, TREE_TYPE (resvar), resvar, integer_one_node); + tmp = build_v (MODIFY_EXPR, resvar, tmp); + + gfc_init_se (&arrayse, NULL); + gfc_copy_loopinfo_to_se (&arrayse, &loop); + arrayse.ss = arrayss; + gfc_conv_expr_val (&arrayse, actual->expr); + tmp = build_v (COND_EXPR, arrayse.expr, tmp, build_empty_stmt ()); + + gfc_add_block_to_block (&body, &arrayse.pre); + gfc_add_expr_to_block (&body, tmp); + gfc_add_block_to_block (&body, &arrayse.post); + + gfc_trans_scalarizing_loops (&loop, &body); + + gfc_add_block_to_block (&se->pre, &loop.pre); + gfc_add_block_to_block (&se->pre, &loop.post); + gfc_cleanup_loop (&loop); + + se->expr = resvar; +} + +/* Inline implementation of the sum and product intrinsics. */ +static void +gfc_conv_intrinsic_arith (gfc_se * se, gfc_expr * expr, int op) +{ + tree resvar; + tree type; + stmtblock_t body; + stmtblock_t block; + tree tmp; + gfc_loopinfo loop; + gfc_actual_arglist *actual; + gfc_ss *arrayss; + gfc_ss *maskss; + gfc_se arrayse; + gfc_se maskse; + gfc_expr *arrayexpr; + gfc_expr *maskexpr; + + if (se->ss) + { + gfc_conv_intrinsic_funcall (se, expr); + return; + } + + type = gfc_typenode_for_spec (&expr->ts); + /* Initialize the result. */ + resvar = gfc_create_var (type, "val"); + if (op == PLUS_EXPR) + tmp = gfc_build_const (type, integer_zero_node); + else + tmp = gfc_build_const (type, integer_one_node); + + gfc_add_modify_expr (&se->pre, resvar, tmp); + + /* Walk the arguments. */ + actual = expr->value.function.actual; + arrayexpr = actual->expr; + arrayss = gfc_walk_expr (arrayexpr); + assert (arrayss != gfc_ss_terminator); + + actual = actual->next->next; + assert (actual); + maskexpr = actual->expr; + if (maskexpr) + { + maskss = gfc_walk_expr (maskexpr); + assert (maskss != gfc_ss_terminator); + } + else + maskss = NULL; + + /* Initialize the scalarizer. */ + gfc_init_loopinfo (&loop); + gfc_add_ss_to_loop (&loop, arrayss); + if (maskss) + gfc_add_ss_to_loop (&loop, maskss); + + /* Initialize the loop. */ + gfc_conv_ss_startstride (&loop); + gfc_conv_loop_setup (&loop); + + gfc_mark_ss_chain_used (arrayss, 1); + if (maskss) + gfc_mark_ss_chain_used (maskss, 1); + /* Generate the loop body. */ + gfc_start_scalarized_body (&loop, &body); + + /* If we have a mask, only add this element if the mask is set. */ + if (maskss) + { + gfc_init_se (&maskse, NULL); + gfc_copy_loopinfo_to_se (&maskse, &loop); + maskse.ss = maskss; + gfc_conv_expr_val (&maskse, maskexpr); + gfc_add_block_to_block (&body, &maskse.pre); + + gfc_start_block (&block); + } + else + gfc_init_block (&block); + + /* Do the actual summation/product. */ + gfc_init_se (&arrayse, NULL); + gfc_copy_loopinfo_to_se (&arrayse, &loop); + arrayse.ss = arrayss; + gfc_conv_expr_val (&arrayse, arrayexpr); + gfc_add_block_to_block (&block, &arrayse.pre); + + tmp = build (op, type, resvar, arrayse.expr); + gfc_add_modify_expr (&block, resvar, tmp); + gfc_add_block_to_block (&block, &arrayse.post); + + if (maskss) + { + /* We enclose the above in if (mask) {...} . */ + tmp = gfc_finish_block (&block); + + tmp = build_v (COND_EXPR, maskse.expr, tmp, build_empty_stmt ()); + } + else + tmp = gfc_finish_block (&block); + gfc_add_expr_to_block (&body, tmp); + + gfc_trans_scalarizing_loops (&loop, &body); + gfc_add_block_to_block (&se->pre, &loop.pre); + gfc_add_block_to_block (&se->pre, &loop.post); + gfc_cleanup_loop (&loop); + + se->expr = resvar; +} + +static void +gfc_conv_intrinsic_minmaxloc (gfc_se * se, gfc_expr * expr, int op) +{ + stmtblock_t body; + stmtblock_t block; + stmtblock_t ifblock; + tree limit; + tree type; + tree tmp; + tree ifbody; + tree cond; + gfc_loopinfo loop; + gfc_actual_arglist *actual; + gfc_ss *arrayss; + gfc_ss *maskss; + gfc_se arrayse; + gfc_se maskse; + gfc_expr *arrayexpr; + gfc_expr *maskexpr; + tree pos; + int n; + + if (se->ss) + { + gfc_conv_intrinsic_funcall (se, expr); + return; + } + + /* Initialize the result. */ + pos = gfc_create_var (gfc_array_index_type, "pos"); + type = gfc_typenode_for_spec (&expr->ts); + + /* Walk the arguments. */ + actual = expr->value.function.actual; + arrayexpr = actual->expr; + arrayss = gfc_walk_expr (arrayexpr); + assert (arrayss != gfc_ss_terminator); + + actual = actual->next->next; + assert (actual); + maskexpr = actual->expr; + if (maskexpr) + { + maskss = gfc_walk_expr (maskexpr); + assert (maskss != gfc_ss_terminator); + } + else + maskss = NULL; + + limit = gfc_create_var (gfc_typenode_for_spec (&arrayexpr->ts), "limit"); + n = gfc_validate_kind (arrayexpr->ts.type, arrayexpr->ts.kind); + switch (arrayexpr->ts.type) + { + case BT_REAL: + tmp = gfc_conv_mpf_to_tree (gfc_real_kinds[n].huge, arrayexpr->ts.kind); + break; + + case BT_INTEGER: + tmp = gfc_conv_mpz_to_tree (gfc_integer_kinds[n].huge, + arrayexpr->ts.kind); + break; + + default: + abort (); + } + + /* Most negative(+HUGE) for maxval, most negative (-HUGE) for minval. */ + if (op == GT_EXPR) + tmp = fold (build1 (NEGATE_EXPR, TREE_TYPE (tmp), tmp)); + gfc_add_modify_expr (&se->pre, limit, tmp); + + /* Initialize the scalarizer. */ + gfc_init_loopinfo (&loop); + gfc_add_ss_to_loop (&loop, arrayss); + if (maskss) + gfc_add_ss_to_loop (&loop, maskss); + + /* Initialize the loop. */ + gfc_conv_ss_startstride (&loop); + gfc_conv_loop_setup (&loop); + + assert (loop.dimen == 1); + + /* Initialize the position to the first element. If the array has zero + size we need to return zero. Otherwise use the first element of the + array, in case all elements are equal to the limit. + ie. pos = (ubound >= lbound) ? lbound, lbound - 1; */ + tmp = fold (build (MINUS_EXPR, gfc_array_index_type, + loop.from[0], integer_one_node)); + cond = fold (build (GE_EXPR, boolean_type_node, + loop.to[0], loop.from[0])); + tmp = fold (build (COND_EXPR, gfc_array_index_type, cond, + loop.from[0], tmp)); + gfc_add_modify_expr (&loop.pre, pos, tmp); + + gfc_mark_ss_chain_used (arrayss, 1); + if (maskss) + gfc_mark_ss_chain_used (maskss, 1); + /* Generate the loop body. */ + gfc_start_scalarized_body (&loop, &body); + + /* If we have a mask, only check this element if the mask is set. */ + if (maskss) + { + gfc_init_se (&maskse, NULL); + gfc_copy_loopinfo_to_se (&maskse, &loop); + maskse.ss = maskss; + gfc_conv_expr_val (&maskse, maskexpr); + gfc_add_block_to_block (&body, &maskse.pre); + + gfc_start_block (&block); + } + else + gfc_init_block (&block); + + /* Compare with the current limit. */ + gfc_init_se (&arrayse, NULL); + gfc_copy_loopinfo_to_se (&arrayse, &loop); + arrayse.ss = arrayss; + gfc_conv_expr_val (&arrayse, arrayexpr); + gfc_add_block_to_block (&block, &arrayse.pre); + + /* We do the following if this is a more extreme value. */ + gfc_start_block (&ifblock); + + /* Assign the value to the limit... */ + gfc_add_modify_expr (&ifblock, limit, arrayse.expr); + + /* Remember where we are. */ + gfc_add_modify_expr (&ifblock, pos, loop.loopvar[0]); + + ifbody = gfc_finish_block (&ifblock); + + /* If it is a more extreme value. */ + tmp = build (op, boolean_type_node, arrayse.expr, limit); + tmp = build_v (COND_EXPR, tmp, ifbody, build_empty_stmt ()); + gfc_add_expr_to_block (&block, tmp); + + if (maskss) + { + /* We enclose the above in if (mask) {...}. */ + tmp = gfc_finish_block (&block); + + tmp = build_v (COND_EXPR, maskse.expr, tmp, build_empty_stmt ()); + } + else + tmp = gfc_finish_block (&block); + gfc_add_expr_to_block (&body, tmp); + + gfc_trans_scalarizing_loops (&loop, &body); + + gfc_add_block_to_block (&se->pre, &loop.pre); + gfc_add_block_to_block (&se->pre, &loop.post); + gfc_cleanup_loop (&loop); + + /* Return a value in the range 1..SIZE(array). */ + tmp = fold (build (MINUS_EXPR, gfc_array_index_type, loop.from[0], + integer_one_node)); + tmp = fold (build (MINUS_EXPR, gfc_array_index_type, pos, tmp)); + /* And convert to the required type. */ + se->expr = convert (type, tmp); +} + +static void +gfc_conv_intrinsic_minmaxval (gfc_se * se, gfc_expr * expr, int op) +{ + tree limit; + tree type; + tree tmp; + tree ifbody; + stmtblock_t body; + stmtblock_t block; + gfc_loopinfo loop; + gfc_actual_arglist *actual; + gfc_ss *arrayss; + gfc_ss *maskss; + gfc_se arrayse; + gfc_se maskse; + gfc_expr *arrayexpr; + gfc_expr *maskexpr; + int n; + + if (se->ss) + { + gfc_conv_intrinsic_funcall (se, expr); + return; + } + + type = gfc_typenode_for_spec (&expr->ts); + /* Initialize the result. */ + limit = gfc_create_var (type, "limit"); + n = gfc_validate_kind (expr->ts.type, expr->ts.kind); + switch (expr->ts.type) + { + case BT_REAL: + tmp = gfc_conv_mpf_to_tree (gfc_real_kinds[n].huge, expr->ts.kind); + break; + + case BT_INTEGER: + tmp = gfc_conv_mpz_to_tree (gfc_integer_kinds[n].huge, expr->ts.kind); + break; + + default: + abort (); + } + + /* Most negative(-HUGE) for maxval, most positive (-HUGE) for minval. */ + if (op == GT_EXPR) + tmp = fold (build1 (NEGATE_EXPR, TREE_TYPE (tmp), tmp)); + gfc_add_modify_expr (&se->pre, limit, tmp); + + /* Walk the arguments. */ + actual = expr->value.function.actual; + arrayexpr = actual->expr; + arrayss = gfc_walk_expr (arrayexpr); + assert (arrayss != gfc_ss_terminator); + + actual = actual->next->next; + assert (actual); + maskexpr = actual->expr; + if (maskexpr) + { + maskss = gfc_walk_expr (maskexpr); + assert (maskss != gfc_ss_terminator); + } + else + maskss = NULL; + + /* Initialize the scalarizer. */ + gfc_init_loopinfo (&loop); + gfc_add_ss_to_loop (&loop, arrayss); + if (maskss) + gfc_add_ss_to_loop (&loop, maskss); + + /* Initialize the loop. */ + gfc_conv_ss_startstride (&loop); + gfc_conv_loop_setup (&loop); + + gfc_mark_ss_chain_used (arrayss, 1); + if (maskss) + gfc_mark_ss_chain_used (maskss, 1); + /* Generate the loop body. */ + gfc_start_scalarized_body (&loop, &body); + + /* If we have a mask, only add this element if the mask is set. */ + if (maskss) + { + gfc_init_se (&maskse, NULL); + gfc_copy_loopinfo_to_se (&maskse, &loop); + maskse.ss = maskss; + gfc_conv_expr_val (&maskse, maskexpr); + gfc_add_block_to_block (&body, &maskse.pre); + + gfc_start_block (&block); + } + else + gfc_init_block (&block); + + /* Compare with the current limit. */ + gfc_init_se (&arrayse, NULL); + gfc_copy_loopinfo_to_se (&arrayse, &loop); + arrayse.ss = arrayss; + gfc_conv_expr_val (&arrayse, arrayexpr); + gfc_add_block_to_block (&block, &arrayse.pre); + + /* Assign the value to the limit... */ + ifbody = build_v (MODIFY_EXPR, limit, arrayse.expr); + + /* If it is a more extreme value. */ + tmp = build (op, boolean_type_node, arrayse.expr, limit); + tmp = build_v (COND_EXPR, tmp, ifbody, build_empty_stmt ()); + gfc_add_expr_to_block (&block, tmp); + gfc_add_block_to_block (&block, &arrayse.post); + + tmp = gfc_finish_block (&block); + if (maskss) + { + /* We enclose the above in if (mask) {...}. */ + tmp = build_v (COND_EXPR, maskse.expr, tmp, build_empty_stmt ()); + } + gfc_add_expr_to_block (&body, tmp); + + gfc_trans_scalarizing_loops (&loop, &body); + + gfc_add_block_to_block (&se->pre, &loop.pre); + gfc_add_block_to_block (&se->pre, &loop.post); + gfc_cleanup_loop (&loop); + + se->expr = limit; +} + +/* BTEST (i, pos) = (i & (1 << pos)) != 0. */ +static void +gfc_conv_intrinsic_btest (gfc_se * se, gfc_expr * expr) +{ + tree arg; + tree arg2; + tree type; + tree tmp; + + arg = gfc_conv_intrinsic_function_args (se, expr); + arg2 = TREE_VALUE (TREE_CHAIN (arg)); + arg = TREE_VALUE (arg); + type = TREE_TYPE (arg); + + tmp = build (LSHIFT_EXPR, type, integer_one_node, arg2); + tmp = build (BIT_AND_EXPR, type, arg, tmp); + tmp = fold (build (NE_EXPR, boolean_type_node, tmp, integer_zero_node)); + type = gfc_typenode_for_spec (&expr->ts); + se->expr = convert (type, tmp); +} + +/* Generate code to perform the specified operation. */ +static void +gfc_conv_intrinsic_bitop (gfc_se * se, gfc_expr * expr, int op) +{ + tree arg; + tree arg2; + tree type; + + arg = gfc_conv_intrinsic_function_args (se, expr); + arg2 = TREE_VALUE (TREE_CHAIN (arg)); + arg = TREE_VALUE (arg); + type = TREE_TYPE (arg); + + se->expr = fold (build (op, type, arg, arg2)); +} + +/* Bitwise not. */ +static void +gfc_conv_intrinsic_not (gfc_se * se, gfc_expr * expr) +{ + tree arg; + + arg = gfc_conv_intrinsic_function_args (se, expr); + arg = TREE_VALUE (arg); + + se->expr = build1 (BIT_NOT_EXPR, TREE_TYPE (arg), arg); +} + +/* Set or clear a single bit. */ +static void +gfc_conv_intrinsic_singlebitop (gfc_se * se, gfc_expr * expr, int set) +{ + tree arg; + tree arg2; + tree type; + tree tmp; + int op; + + arg = gfc_conv_intrinsic_function_args (se, expr); + arg2 = TREE_VALUE (TREE_CHAIN (arg)); + arg = TREE_VALUE (arg); + type = TREE_TYPE (arg); + + tmp = fold (build (LSHIFT_EXPR, type, integer_one_node, arg2)); + if (set) + op = BIT_IOR_EXPR; + else + { + op = BIT_AND_EXPR; + tmp = fold (build1 (BIT_NOT_EXPR, type, tmp)); + } + se->expr = fold (build (op, type, arg, tmp)); +} + +/* Extract a sequence of bits. + IBITS(I, POS, LEN) = (I >> POS) & ~((~0) << LEN). */ +static void +gfc_conv_intrinsic_ibits (gfc_se * se, gfc_expr * expr) +{ + tree arg; + tree arg2; + tree arg3; + tree type; + tree tmp; + tree mask; + + arg = gfc_conv_intrinsic_function_args (se, expr); + arg2 = TREE_CHAIN (arg); + arg3 = TREE_VALUE (TREE_CHAIN (arg2)); + arg = TREE_VALUE (arg); + arg2 = TREE_VALUE (arg2); + type = TREE_TYPE (arg); + + mask = build_int_2 (-1, ~(unsigned HOST_WIDE_INT) 0); + mask = build (LSHIFT_EXPR, type, mask, arg3); + mask = build1 (BIT_NOT_EXPR, type, mask); + + tmp = build (RSHIFT_EXPR, type, arg, arg2); + + se->expr = fold (build (BIT_AND_EXPR, type, tmp, mask)); +} + +/* ISHFT (I, SHIFT) = (shift >= 0) ? i << shift : i >> -shift. */ +static void +gfc_conv_intrinsic_ishft (gfc_se * se, gfc_expr * expr) +{ + tree arg; + tree arg2; + tree type; + tree tmp; + tree lshift; + tree rshift; + + arg = gfc_conv_intrinsic_function_args (se, expr); + arg2 = TREE_VALUE (TREE_CHAIN (arg)); + arg = TREE_VALUE (arg); + type = TREE_TYPE (arg); + + /* Left shift if positive. */ + lshift = build (LSHIFT_EXPR, type, arg, arg2); + + /* Right shift if negative. This will perform an arithmetic shift as + we are dealing with signed integers. Section 13.5.7 allows this. */ + tmp = build1 (NEGATE_EXPR, TREE_TYPE (arg2), arg2); + rshift = build (RSHIFT_EXPR, type, arg, tmp); + + tmp = build (GT_EXPR, boolean_type_node, arg2, integer_zero_node); + rshift = build (COND_EXPR, type, tmp, lshift, rshift); + + /* Do nothing if shift == 0. */ + tmp = build (EQ_EXPR, boolean_type_node, arg2, integer_zero_node); + se->expr = build (COND_EXPR, type, tmp, arg, rshift); +} + +/* Circular shift. AKA rotate or barrel shift. */ +static void +gfc_conv_intrinsic_ishftc (gfc_se * se, gfc_expr * expr) +{ + tree arg; + tree arg2; + tree arg3; + tree type; + tree tmp; + tree lrot; + tree rrot; + + arg = gfc_conv_intrinsic_function_args (se, expr); + arg2 = TREE_CHAIN (arg); + arg3 = TREE_CHAIN (arg2); + if (arg3) + { + /* Use a library function for the 3 parameter version. */ + type = TREE_TYPE (TREE_VALUE (arg)); + /* Convert all args to the same type otherwise we need loads of library + functions. SIZE and SHIFT cannot have values > BIT_SIZE (I) so the + conversion is safe. */ + tmp = convert (type, TREE_VALUE (arg2)); + TREE_VALUE (arg2) = tmp; + tmp = convert (type, TREE_VALUE (arg3)); + TREE_VALUE (arg3) = tmp; + + switch (expr->ts.kind) + { + case 4: + tmp = gfor_fndecl_math_ishftc4; + break; + case 8: + tmp = gfor_fndecl_math_ishftc8; + break; + default: + abort (); + } + se->expr = gfc_build_function_call (tmp, arg); + return; + } + arg = TREE_VALUE (arg); + arg2 = TREE_VALUE (arg2); + type = TREE_TYPE (arg); + + /* Rotate left if positive. */ + lrot = build (LROTATE_EXPR, type, arg, arg2); + + /* Rotate right if negative. */ + tmp = build1 (NEGATE_EXPR, TREE_TYPE (arg2), arg2); + rrot = build (RROTATE_EXPR, type, arg, tmp); + + tmp = build (GT_EXPR, boolean_type_node, arg2, integer_zero_node); + rrot = build (COND_EXPR, type, tmp, lrot, rrot); + + /* Do nothing if shift == 0. */ + tmp = build (EQ_EXPR, boolean_type_node, arg2, integer_zero_node); + se->expr = build (COND_EXPR, type, tmp, arg, rrot); +} + +/* The length of a character string. */ +static void +gfc_conv_intrinsic_len (gfc_se * se, gfc_expr * expr) +{ + tree len; + tree type; + tree decl; + gfc_symbol *sym; + gfc_se argse; + gfc_expr *arg; + + assert (!se->ss); + + arg = expr->value.function.actual->expr; + + type = gfc_typenode_for_spec (&expr->ts); + switch (arg->expr_type) + { + case EXPR_CONSTANT: + len = build_int_2 (arg->value.character.length, 0); + break; + + default: + if (arg->expr_type == EXPR_VARIABLE && arg->ref == NULL) + { + sym = arg->symtree->n.sym; + decl = gfc_get_symbol_decl (sym); + if (decl == current_function_decl && sym->attr.function + && (sym->result == sym)) + decl = gfc_get_fake_result_decl (sym); + + len = sym->ts.cl->backend_decl; + assert (len); + } + else + { + /* Anybody stupid enough to do this deserves inefficient code. */ + gfc_init_se (&argse, se); + gfc_conv_expr (&argse, arg); + gfc_add_block_to_block (&se->pre, &argse.pre); + gfc_add_block_to_block (&se->post, &argse.post); + len = argse.string_length; + } + break; + } + se->expr = convert (type, len); +} + +/* The length of a character string not including trailing blanks. */ +static void +gfc_conv_intrinsic_len_trim (gfc_se * se, gfc_expr * expr) +{ + tree args; + tree type; + + args = gfc_conv_intrinsic_function_args (se, expr); + type = gfc_typenode_for_spec (&expr->ts); + se->expr = gfc_build_function_call (gfor_fndecl_string_len_trim, args); + se->expr = convert (type, se->expr); +} + + +/* Returns the starting position of a substring within a string. */ + +static void +gfc_conv_intrinsic_index (gfc_se * se, gfc_expr * expr) +{ + tree args; + tree back; + tree type; + tree tmp; + + args = gfc_conv_intrinsic_function_args (se, expr); + type = gfc_typenode_for_spec (&expr->ts); + tmp = gfc_advance_chain (args, 3); + if (TREE_CHAIN (tmp) == NULL_TREE) + { + back = convert (gfc_logical4_type_node, integer_one_node); + back = tree_cons (NULL_TREE, integer_zero_node, NULL_TREE); + TREE_CHAIN (tmp) = back; + } + else + { + back = TREE_CHAIN (tmp); + TREE_VALUE (back) = convert (gfc_logical4_type_node, TREE_VALUE (back)); + } + + se->expr = gfc_build_function_call (gfor_fndecl_string_index, args); + se->expr = convert (type, se->expr); +} + +/* The ascii value for a single character. */ +static void +gfc_conv_intrinsic_ichar (gfc_se * se, gfc_expr * expr) +{ + tree arg; + tree type; + + arg = gfc_conv_intrinsic_function_args (se, expr); + arg = TREE_VALUE (TREE_CHAIN (arg)); + assert (POINTER_TYPE_P (TREE_TYPE (arg))); + arg = build1 (NOP_EXPR, pchar_type_node, arg); + type = gfc_typenode_for_spec (&expr->ts); + + se->expr = gfc_build_indirect_ref (arg); + se->expr = convert (type, se->expr); +} + + +/* MERGE (tsource, fsource, mask) = mask ? tsource : fsource. */ + +static void +gfc_conv_intrinsic_merge (gfc_se * se, gfc_expr * expr) +{ + tree arg; + tree tsource; + tree fsource; + tree mask; + tree type; + + arg = gfc_conv_intrinsic_function_args (se, expr); + tsource = TREE_VALUE (arg); + arg = TREE_CHAIN (arg); + fsource = TREE_VALUE (arg); + arg = TREE_CHAIN (arg); + mask = TREE_VALUE (arg); + + type = TREE_TYPE (tsource); + se->expr = fold (build (COND_EXPR, type, mask, tsource, fsource)); +} + + +static void +gfc_conv_intrinsic_size (gfc_se * se, gfc_expr * expr) +{ + gfc_actual_arglist *actual; + tree args; + tree type; + tree fndecl; + gfc_se argse; + gfc_ss *ss; + + gfc_init_se (&argse, NULL); + actual = expr->value.function.actual; + + ss = gfc_walk_expr (actual->expr); + assert (ss != gfc_ss_terminator); + argse.want_pointer = 1; + gfc_conv_expr_descriptor (&argse, actual->expr, ss); + gfc_add_block_to_block (&se->pre, &argse.pre); + gfc_add_block_to_block (&se->post, &argse.post); + args = gfc_chainon_list (NULL_TREE, argse.expr); + + actual = actual->next; + if (actual->expr) + { + gfc_init_se (&argse, NULL); + gfc_conv_expr_type (&argse, actual->expr, gfc_array_index_type); + gfc_add_block_to_block (&se->pre, &argse.pre); + args = gfc_chainon_list (args, argse.expr); + fndecl = gfor_fndecl_size1; + } + else + fndecl = gfor_fndecl_size0; + + se->expr = gfc_build_function_call (fndecl, args); + type = gfc_typenode_for_spec (&expr->ts); + se->expr = convert (type, se->expr); +} + + +/* Intrinsic string comparison functions. */ + + static void +gfc_conv_intrinsic_strcmp (gfc_se * se, gfc_expr * expr, int op) +{ + tree type; + tree args; + + args = gfc_conv_intrinsic_function_args (se, expr); + /* Build a call for the comparison. */ + se->expr = gfc_build_function_call (gfor_fndecl_compare_string, args); + + type = gfc_typenode_for_spec (&expr->ts); + se->expr = build (op, type, se->expr, integer_zero_node); +} + +/* Generate a call to the adjustl/adjustr library function. */ +static void +gfc_conv_intrinsic_adjust (gfc_se * se, gfc_expr * expr, tree fndecl) +{ + tree args; + tree len; + tree type; + tree var; + tree tmp; + + args = gfc_conv_intrinsic_function_args (se, expr); + len = TREE_VALUE (args); + + type = TREE_TYPE (TREE_VALUE (TREE_CHAIN (args))); + var = gfc_conv_string_tmp (se, type, len); + args = tree_cons (NULL_TREE, var, args); + + tmp = gfc_build_function_call (fndecl, args); + gfc_add_expr_to_block (&se->pre, tmp); + se->expr = var; + se->string_length = len; +} + + +/* Scalar transfer statement. + TRANSFER (source, mold) = *(typeof<mould> *)&source */ + +static void +gfc_conv_intrinsic_transfer (gfc_se * se, gfc_expr * expr) +{ + gfc_actual_arglist *arg; + gfc_se argse; + tree type; + tree ptr; + gfc_ss *ss; + + assert (!se->ss); + + /* Get a pointer to the source. */ + arg = expr->value.function.actual; + ss = gfc_walk_expr (arg->expr); + gfc_init_se (&argse, NULL); + if (ss == gfc_ss_terminator) + gfc_conv_expr_reference (&argse, arg->expr); + else + gfc_conv_array_parameter (&argse, arg->expr, ss, 1); + gfc_add_block_to_block (&se->pre, &argse.pre); + gfc_add_block_to_block (&se->post, &argse.post); + ptr = argse.expr; + + arg = arg->next; + type = gfc_typenode_for_spec (&expr->ts); + ptr = convert (build_pointer_type (type), ptr); + if (expr->ts.type == BT_CHARACTER) + { + gfc_init_se (&argse, NULL); + gfc_conv_expr (&argse, arg->expr); + gfc_add_block_to_block (&se->pre, &argse.pre); + gfc_add_block_to_block (&se->post, &argse.post); + se->expr = ptr; + se->string_length = argse.string_length; + } + else + { + se->expr = gfc_build_indirect_ref (ptr); + } +} + + +/* Generate code for the ALLOCATED intrinsic. + Generate inline code that directly check the address of the argument. */ + +static void +gfc_conv_allocated (gfc_se *se, gfc_expr *expr) +{ + gfc_actual_arglist *arg1; + gfc_se arg1se; + gfc_ss *ss1; + tree tmp; + + gfc_init_se (&arg1se, NULL); + arg1 = expr->value.function.actual; + ss1 = gfc_walk_expr (arg1->expr); + arg1se.descriptor_only = 1; + gfc_conv_expr_descriptor (&arg1se, arg1->expr, ss1); + + tmp = gfc_conv_descriptor_data (arg1se.expr); + tmp = build (NE_EXPR, boolean_type_node, tmp, null_pointer_node); + se->expr = convert (gfc_typenode_for_spec (&expr->ts), tmp); +} + + +/* Generate code for the ASSOCIATED intrinsic. + If both POINTER and TARGET are arrays, generate a call to library function + _gfor_associated, and pass descriptors of POINTER and TARGET to it. + In other cases, generate inline code that directly compare the address of + POINTER with the address of TARGET. */ + +static void +gfc_conv_associated (gfc_se *se, gfc_expr *expr) +{ + gfc_actual_arglist *arg1; + gfc_actual_arglist *arg2; + gfc_se arg1se; + gfc_se arg2se; + tree tmp2; + tree tmp; + tree args, fndecl; + gfc_ss *ss1, *ss2; + + gfc_init_se (&arg1se, NULL); + gfc_init_se (&arg2se, NULL); + arg1 = expr->value.function.actual; + arg2 = arg1->next; + ss1 = gfc_walk_expr (arg1->expr); + + if (!arg2->expr) + { + /* No optional target. */ + if (ss1 == gfc_ss_terminator) + { + /* A pointer to a scalar. */ + arg1se.want_pointer = 1; + gfc_conv_expr (&arg1se, arg1->expr); + tmp2 = arg1se.expr; + } + else + { + /* A pointer to an array. */ + arg1se.descriptor_only = 1; + gfc_conv_expr_lhs (&arg1se, arg1->expr); + tmp2 = gfc_conv_descriptor_data (arg1se.expr); + } + tmp = build (NE_EXPR, boolean_type_node, tmp2, null_pointer_node); + se->expr = tmp; + } + else + { + /* An optional target. */ + ss2 = gfc_walk_expr (arg2->expr); + if (ss1 == gfc_ss_terminator) + { + /* A pointer to a scalar. */ + assert (ss2 == gfc_ss_terminator); + arg1se.want_pointer = 1; + gfc_conv_expr (&arg1se, arg1->expr); + arg2se.want_pointer = 1; + gfc_conv_expr (&arg2se, arg2->expr); + tmp = build (EQ_EXPR, boolean_type_node, arg1se.expr, arg2se.expr); + se->expr = tmp; + } + else + { + /* A pointer to an array, call library function _gfor_associated. */ + assert (ss2 != gfc_ss_terminator); + args = NULL_TREE; + arg1se.want_pointer = 1; + gfc_conv_expr_descriptor (&arg1se, arg1->expr, ss1); + args = gfc_chainon_list (args, arg1se.expr); + arg2se.want_pointer = 1; + gfc_conv_expr_descriptor (&arg2se, arg2->expr, ss2); + gfc_add_block_to_block (&se->pre, &arg2se.pre); + gfc_add_block_to_block (&se->post, &arg2se.post); + args = gfc_chainon_list (args, arg2se.expr); + fndecl = gfor_fndecl_associated; + se->expr = gfc_build_function_call (fndecl, args); + } + } + se->expr = convert (gfc_typenode_for_spec (&expr->ts), se->expr); +} + + +/* Scan a string for any one of the characters in a set of characters. */ + +static void +gfc_conv_intrinsic_scan (gfc_se * se, gfc_expr * expr) +{ + tree args; + tree back; + tree type; + tree tmp; + + args = gfc_conv_intrinsic_function_args (se, expr); + type = gfc_typenode_for_spec (&expr->ts); + tmp = gfc_advance_chain (args, 3); + if (TREE_CHAIN (tmp) == NULL_TREE) + { + back = convert (gfc_logical4_type_node, integer_one_node); + back = tree_cons (NULL_TREE, integer_zero_node, NULL_TREE); + TREE_CHAIN (tmp) = back; + } + else + { + back = TREE_CHAIN (tmp); + TREE_VALUE (back) = convert (gfc_logical4_type_node, TREE_VALUE (back)); + } + + se->expr = gfc_build_function_call (gfor_fndecl_string_scan, args); + se->expr = convert (type, se->expr); +} + + +/* Verify that a set of characters contains all the characters in a string + by indentifying the position of the first character in a string of + characters that does not appear in a given set of characters. */ + +static void +gfc_conv_intrinsic_verify (gfc_se * se, gfc_expr * expr) +{ + tree args; + tree back; + tree type; + tree tmp; + + args = gfc_conv_intrinsic_function_args (se, expr); + type = gfc_typenode_for_spec (&expr->ts); + tmp = gfc_advance_chain (args, 3); + if (TREE_CHAIN (tmp) == NULL_TREE) + { + back = convert (gfc_logical4_type_node, integer_one_node); + back = tree_cons (NULL_TREE, integer_zero_node, NULL_TREE); + TREE_CHAIN (tmp) = back; + } + else + { + back = TREE_CHAIN (tmp); + TREE_VALUE (back) = convert (gfc_logical4_type_node, TREE_VALUE (back)); + } + + se->expr = gfc_build_function_call (gfor_fndecl_string_verify, args); + se->expr = convert (type, se->expr); +} + +/* Prepare components and related information of a real number which is + the first argument of a elemental functions to manipulate reals. */ + +static +void prepare_arg_info (gfc_se * se, gfc_expr * expr, + real_compnt_info * rcs, int all) +{ + tree arg; + tree masktype; + tree tmp; + tree wbits; + tree one; + tree exponent, fraction; + int n; + gfc_expr *a1; + + if (TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT) + gfc_todo_error ("Non-IEEE floating format"); + + assert (expr->expr_type == EXPR_FUNCTION); + + arg = gfc_conv_intrinsic_function_args (se, expr); + arg = TREE_VALUE (arg); + rcs->type = TREE_TYPE (arg); + + /* Force arg'type to integer by unaffected convert */ + a1 = expr->value.function.actual->expr; + masktype = gfc_get_int_type (a1->ts.kind); + rcs->mtype = masktype; + tmp = build1 (VIEW_CONVERT_EXPR, masktype, arg); + arg = gfc_create_var (masktype, "arg"); + gfc_add_modify_expr(&se->pre, arg, tmp); + rcs->arg = arg; + + /* Caculate the numbers of bits of exponent, fraction and word */ + n = gfc_validate_kind (a1->ts.type, a1->ts.kind); + tmp = build_int_2 (gfc_real_kinds[n].digits - 1, 0); + rcs->fdigits = convert (masktype, tmp); + wbits = build_int_2 (TYPE_PRECISION (rcs->type) - 1, 0); + wbits = convert (masktype, wbits); + rcs->edigits = fold (build (MINUS_EXPR, masktype, wbits, tmp)); + + /* Form masks for exponent/fraction/sign */ + one = gfc_build_const (masktype, integer_one_node); + rcs->smask = fold (build (LSHIFT_EXPR, masktype, one, wbits)); + rcs->f1 = fold (build (LSHIFT_EXPR, masktype, one, rcs->fdigits)); + rcs->emask = fold (build (MINUS_EXPR, masktype, rcs->smask, rcs->f1)); + rcs->fmask = fold (build (MINUS_EXPR, masktype, rcs->f1, one)); + /* Form bias. */ + tmp = fold (build (MINUS_EXPR, masktype, rcs->edigits, one)); + tmp = fold (build (LSHIFT_EXPR, masktype, one, tmp)); + rcs->bias = fold (build (MINUS_EXPR, masktype, tmp ,one)); + + if (all) + { + /* exponent, and fraction */ + tmp = build (BIT_AND_EXPR, masktype, arg, rcs->emask); + tmp = build (RSHIFT_EXPR, masktype, tmp, rcs->fdigits); + exponent = gfc_create_var (masktype, "exponent"); + gfc_add_modify_expr(&se->pre, exponent, tmp); + rcs->expn = exponent; + + tmp = build (BIT_AND_EXPR, masktype, arg, rcs->fmask); + fraction = gfc_create_var (masktype, "fraction"); + gfc_add_modify_expr(&se->pre, fraction, tmp); + rcs->frac = fraction; + } +} + +/* Build a call to __builtin_clz. */ + +static tree +call_builtin_clz (tree result_type, tree op0) +{ + tree fn, parms, call; + enum machine_mode op0_mode = TYPE_MODE (TREE_TYPE (op0)); + + if (op0_mode == TYPE_MODE (integer_type_node)) + fn = built_in_decls[BUILT_IN_CLZ]; + else if (op0_mode == TYPE_MODE (long_integer_type_node)) + fn = built_in_decls[BUILT_IN_CLZL]; + else if (op0_mode == TYPE_MODE (long_long_integer_type_node)) + fn = built_in_decls[BUILT_IN_CLZLL]; + else + abort (); + + parms = tree_cons (NULL, op0, NULL); + call = gfc_build_function_call (fn, parms); + + return convert (result_type, call); +} + +/* Generate code for SPACING (X) intrinsic function. We generate: + + t = expn - (BITS_OF_FRACTION) + res = t << (BITS_OF_FRACTION) + if (t < 0) + res = tiny(X) +*/ + +static void +gfc_conv_intrinsic_spacing (gfc_se * se, gfc_expr * expr) +{ + tree arg; + tree masktype; + tree tmp, t1, cond; + tree tiny, zero; + tree fdigits; + real_compnt_info rcs; + + prepare_arg_info (se, expr, &rcs, 0); + arg = rcs.arg; + masktype = rcs.mtype; + fdigits = rcs.fdigits; + tiny = rcs.f1; + zero = gfc_build_const (masktype, integer_zero_node); + tmp = build (BIT_AND_EXPR, masktype, rcs.emask, arg); + tmp = build (RSHIFT_EXPR, masktype, tmp, fdigits); + tmp = build (MINUS_EXPR, masktype, tmp, fdigits); + cond = build (LE_EXPR, boolean_type_node, tmp, zero); + t1 = build (LSHIFT_EXPR, masktype, tmp, fdigits); + tmp = build (COND_EXPR, masktype, cond, tiny, t1); + tmp = build1 (VIEW_CONVERT_EXPR, rcs.type, tmp); + + se->expr = tmp; +} + +/* Generate code for RRSPACING (X) intrinsic function. We generate: + sedigits = edigits + 1; + if (expn == 0) + { + t1 = leadzero (frac); + frac = frac << (t1 + sedigits); + frac = frac >> (sedigits); + } + t = bias + BITS_OF_FRACTION_OF; + res = (t << BITS_OF_FRACTION_OF) | frac; +*/ + +static void +gfc_conv_intrinsic_rrspacing (gfc_se * se, gfc_expr * expr) +{ + tree masktype; + tree tmp, t1, t2, cond; + tree one, zero; + tree fdigits, fraction; + real_compnt_info rcs; + + prepare_arg_info (se, expr, &rcs, 1); + masktype = rcs.mtype; + fdigits = rcs.fdigits; + fraction = rcs.frac; + one = gfc_build_const (masktype, integer_one_node); + zero = gfc_build_const (masktype, integer_zero_node); + t2 = build (PLUS_EXPR, masktype, rcs.edigits, one); + + t1 = call_builtin_clz (masktype, fraction); + tmp = build (PLUS_EXPR, masktype, t1, one); + tmp = build (LSHIFT_EXPR, masktype, fraction, tmp); + tmp = build (RSHIFT_EXPR, masktype, tmp, t2); + cond = build (EQ_EXPR, boolean_type_node, rcs.expn, zero); + fraction = build (COND_EXPR, masktype, cond, tmp, fraction); + + tmp = build (PLUS_EXPR, masktype, rcs.bias, fdigits); + tmp = build (LSHIFT_EXPR, masktype, tmp, fdigits); + tmp = build (BIT_IOR_EXPR, masktype, tmp, fraction); + + tmp = build1 (VIEW_CONVERT_EXPR, rcs.type, tmp); + se->expr = tmp; +} + +/* Generate code for SELECTED_INT_KIND (R) intrinsic function. */ + +static void +gfc_conv_intrinsic_si_kind (gfc_se * se, gfc_expr * expr) +{ + tree args; + + args = gfc_conv_intrinsic_function_args (se, expr); + args = TREE_VALUE (args); + args = gfc_build_addr_expr (NULL, args); + args = tree_cons (NULL_TREE, args, NULL_TREE); + se->expr = gfc_build_function_call (gfor_fndecl_si_kind, args); +} + +/* Generate code for SELECTED_REAL_KIND (P, R) intrinsic function. */ + +static void +gfc_conv_intrinsic_sr_kind (gfc_se * se, gfc_expr * expr) +{ + gfc_actual_arglist *actual; + tree args; + gfc_se argse; + + args = NULL_TREE; + for (actual = expr->value.function.actual; actual; actual = actual->next) + { + gfc_init_se (&argse, se); + + /* Pass a NULL pointer for an absent arg. */ + if (actual->expr == NULL) + argse.expr = null_pointer_node; + else + gfc_conv_expr_reference (&argse, actual->expr); + + gfc_add_block_to_block (&se->pre, &argse.pre); + gfc_add_block_to_block (&se->post, &argse.post); + args = gfc_chainon_list (args, argse.expr); + } + se->expr = gfc_build_function_call (gfor_fndecl_sr_kind, args); +} + + +/* Generate code for TRIM (A) intrinsic function. */ + +static void +gfc_conv_intrinsic_trim (gfc_se * se, gfc_expr * expr) +{ + tree var; + tree len; + tree addr; + tree tmp; + tree arglist; + tree type; + tree cond; + + arglist = NULL_TREE; + + type = build_pointer_type (gfc_character1_type_node); + var = gfc_create_var (type, "pstr"); + addr = gfc_build_addr_expr (ppvoid_type_node, var); + len = gfc_create_var (gfc_int4_type_node, "len"); + + tmp = gfc_conv_intrinsic_function_args (se, expr); + arglist = gfc_chainon_list (arglist, gfc_build_addr_expr (NULL, len)); + arglist = gfc_chainon_list (arglist, addr); + arglist = chainon (arglist, tmp); + + tmp = gfc_build_function_call (gfor_fndecl_string_trim, arglist); + gfc_add_expr_to_block (&se->pre, tmp); + + /* Free the temporary afterwards, if necessary. */ + cond = build (GT_EXPR, boolean_type_node, len, integer_zero_node); + arglist = gfc_chainon_list (NULL_TREE, var); + tmp = gfc_build_function_call (gfor_fndecl_internal_free, arglist); + tmp = build_v (COND_EXPR, cond, tmp, build_empty_stmt ()); + gfc_add_expr_to_block (&se->post, tmp); + + se->expr = var; + se->string_length = len; +} + + +/* Generate code for REPEAT (STRING, NCOPIES) intrinsic function. */ + +static void +gfc_conv_intrinsic_repeat (gfc_se * se, gfc_expr * expr) +{ + tree tmp; + tree len; + tree args; + tree arglist; + tree ncopies; + tree var; + tree type; + + args = gfc_conv_intrinsic_function_args (se, expr); + len = TREE_VALUE (args); + tmp = gfc_advance_chain (args, 2); + ncopies = TREE_VALUE (tmp); + len = fold (build (MULT_EXPR, gfc_int4_type_node, len, ncopies)); + type = gfc_get_character_type (expr->ts.kind, expr->ts.cl); + var = gfc_conv_string_tmp (se, build_pointer_type (type), len); + + arglist = NULL_TREE; + arglist = gfc_chainon_list (arglist, var); + arglist = chainon (arglist, args); + tmp = gfc_build_function_call (gfor_fndecl_string_repeat, arglist); + gfc_add_expr_to_block (&se->pre, tmp); + + se->expr = var; + se->string_length = len; +} + + +/* 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. */ + +void +gfc_conv_intrinsic_function (gfc_se * se, gfc_expr * expr) +{ + gfc_intrinsic_sym *isym; + char *name; + int lib; + + isym = expr->value.function.isym; + + name = &expr->value.function.name[2]; + + if (expr->rank > 0) + { + lib = gfc_is_intrinsic_libcall (expr); + if (lib != 0) + { + if (lib == 1) + se->ignore_optional = 1; + gfc_conv_intrinsic_funcall (se, expr); + return; + } + } + + switch (expr->value.function.isym->generic_id) + { + case GFC_ISYM_NONE: + abort (); + + case GFC_ISYM_REPEAT: + gfc_conv_intrinsic_repeat (se, expr); + break; + + case GFC_ISYM_TRIM: + gfc_conv_intrinsic_trim (se, expr); + break; + + case GFC_ISYM_SI_KIND: + gfc_conv_intrinsic_si_kind (se, expr); + break; + + case GFC_ISYM_SR_KIND: + gfc_conv_intrinsic_sr_kind (se, expr); + break; + + case GFC_ISYM_EXPONENT: + gfc_conv_intrinsic_exponent (se, expr); + break; + + case GFC_ISYM_SPACING: + gfc_conv_intrinsic_spacing (se, expr); + break; + + case GFC_ISYM_RRSPACING: + gfc_conv_intrinsic_rrspacing (se, expr); + break; + + case GFC_ISYM_SCAN: + gfc_conv_intrinsic_scan (se, expr); + break; + + case GFC_ISYM_VERIFY: + gfc_conv_intrinsic_verify (se, expr); + break; + + case GFC_ISYM_ALLOCATED: + gfc_conv_allocated (se, expr); + break; + + case GFC_ISYM_ASSOCIATED: + gfc_conv_associated(se, expr); + break; + + case GFC_ISYM_ABS: + gfc_conv_intrinsic_abs (se, expr); + break; + + case GFC_ISYM_ADJUSTL: + gfc_conv_intrinsic_adjust (se, expr, gfor_fndecl_adjustl); + break; + + case GFC_ISYM_ADJUSTR: + gfc_conv_intrinsic_adjust (se, expr, gfor_fndecl_adjustr); + break; + + case GFC_ISYM_AIMAG: + gfc_conv_intrinsic_imagpart (se, expr); + break; + + case GFC_ISYM_AINT: + gfc_conv_intrinsic_aint (se, expr, FIX_TRUNC_EXPR); + break; + + case GFC_ISYM_ALL: + gfc_conv_intrinsic_anyall (se, expr, EQ_EXPR); + break; + + case GFC_ISYM_ANINT: + gfc_conv_intrinsic_aint (se, expr, FIX_ROUND_EXPR); + break; + + case GFC_ISYM_ANY: + gfc_conv_intrinsic_anyall (se, expr, NE_EXPR); + break; + + case GFC_ISYM_BTEST: + gfc_conv_intrinsic_btest (se, expr); + break; + + case GFC_ISYM_ACHAR: + case GFC_ISYM_CHAR: + gfc_conv_intrinsic_char (se, expr); + break; + + case GFC_ISYM_CONVERSION: + case GFC_ISYM_REAL: + case GFC_ISYM_LOGICAL: + case GFC_ISYM_DBLE: + gfc_conv_intrinsic_conversion (se, expr); + break; + + /* Integer conversions are handled seperately to make sure we get the + correct rounding mode. */ + case GFC_ISYM_INT: + gfc_conv_intrinsic_int (se, expr, FIX_TRUNC_EXPR); + break; + + case GFC_ISYM_NINT: + gfc_conv_intrinsic_int (se, expr, FIX_ROUND_EXPR); + break; + + case GFC_ISYM_CEILING: + gfc_conv_intrinsic_int (se, expr, FIX_CEIL_EXPR); + break; + + case GFC_ISYM_FLOOR: + gfc_conv_intrinsic_int (se, expr, FIX_FLOOR_EXPR); + break; + + case GFC_ISYM_MOD: + gfc_conv_intrinsic_mod (se, expr, 0); + break; + + case GFC_ISYM_MODULO: + gfc_conv_intrinsic_mod (se, expr, 1); + break; + + case GFC_ISYM_CMPLX: + gfc_conv_intrinsic_cmplx (se, expr, name[5] == '1'); + break; + + case GFC_ISYM_CONJG: + gfc_conv_intrinsic_conjg (se, expr); + break; + + case GFC_ISYM_COUNT: + gfc_conv_intrinsic_count (se, expr); + break; + + case GFC_ISYM_DIM: + gfc_conv_intrinsic_dim (se, expr); + break; + + case GFC_ISYM_DPROD: + gfc_conv_intrinsic_dprod (se, expr); + break; + + case GFC_ISYM_IAND: + gfc_conv_intrinsic_bitop (se, expr, BIT_AND_EXPR); + break; + + case GFC_ISYM_IBCLR: + gfc_conv_intrinsic_singlebitop (se, expr, 0); + break; + + case GFC_ISYM_IBITS: + gfc_conv_intrinsic_ibits (se, expr); + break; + + case GFC_ISYM_IBSET: + gfc_conv_intrinsic_singlebitop (se, expr, 1); + break; + + case GFC_ISYM_IACHAR: + case GFC_ISYM_ICHAR: + /* We assume ASCII character sequence. */ + gfc_conv_intrinsic_ichar (se, expr); + break; + + case GFC_ISYM_IEOR: + gfc_conv_intrinsic_bitop (se, expr, BIT_XOR_EXPR); + break; + + case GFC_ISYM_INDEX: + gfc_conv_intrinsic_index (se, expr); + break; + + case GFC_ISYM_IOR: + gfc_conv_intrinsic_bitop (se, expr, BIT_IOR_EXPR); + break; + + case GFC_ISYM_ISHFT: + gfc_conv_intrinsic_ishft (se, expr); + break; + + case GFC_ISYM_ISHFTC: + gfc_conv_intrinsic_ishftc (se, expr); + break; + + case GFC_ISYM_LBOUND: + gfc_conv_intrinsic_bound (se, expr, 0); + break; + + case GFC_ISYM_LEN: + gfc_conv_intrinsic_len (se, expr); + break; + + case GFC_ISYM_LEN_TRIM: + gfc_conv_intrinsic_len_trim (se, expr); + break; + + case GFC_ISYM_LGE: + gfc_conv_intrinsic_strcmp (se, expr, GE_EXPR); + break; + + case GFC_ISYM_LGT: + gfc_conv_intrinsic_strcmp (se, expr, GT_EXPR); + break; + + case GFC_ISYM_LLE: + gfc_conv_intrinsic_strcmp (se, expr, LE_EXPR); + break; + + case GFC_ISYM_LLT: + gfc_conv_intrinsic_strcmp (se, expr, LT_EXPR); + break; + + case GFC_ISYM_MAX: + gfc_conv_intrinsic_minmax (se, expr, GT_EXPR); + break; + + case GFC_ISYM_MAXLOC: + gfc_conv_intrinsic_minmaxloc (se, expr, GT_EXPR); + break; + + case GFC_ISYM_MAXVAL: + gfc_conv_intrinsic_minmaxval (se, expr, GT_EXPR); + break; + + case GFC_ISYM_MERGE: + gfc_conv_intrinsic_merge (se, expr); + break; + + case GFC_ISYM_MIN: + gfc_conv_intrinsic_minmax (se, expr, LT_EXPR); + break; + + case GFC_ISYM_MINLOC: + gfc_conv_intrinsic_minmaxloc (se, expr, LT_EXPR); + break; + + case GFC_ISYM_MINVAL: + gfc_conv_intrinsic_minmaxval (se, expr, LT_EXPR); + break; + + case GFC_ISYM_NOT: + gfc_conv_intrinsic_not (se, expr); + break; + + case GFC_ISYM_PRESENT: + gfc_conv_intrinsic_present (se, expr); + break; + + case GFC_ISYM_PRODUCT: + gfc_conv_intrinsic_arith (se, expr, MULT_EXPR); + break; + + case GFC_ISYM_SIGN: + gfc_conv_intrinsic_sign (se, expr); + break; + + case GFC_ISYM_SIZE: + gfc_conv_intrinsic_size (se, expr); + break; + + case GFC_ISYM_SUM: + gfc_conv_intrinsic_arith (se, expr, PLUS_EXPR); + break; + + case GFC_ISYM_TRANSFER: + gfc_conv_intrinsic_transfer (se, expr); + break; + + case GFC_ISYM_UBOUND: + gfc_conv_intrinsic_bound (se, expr, 1); + break; + + case GFC_ISYM_DOT_PRODUCT: + case GFC_ISYM_MATMUL: + gfc_conv_intrinsic_funcall (se, expr); + break; + + default: + gfc_conv_intrinsic_lib_function (se, expr); + break; + } +} + + +/* This generates code to execute before entering the scalarization loop. + Currently does nothing. */ + +void +gfc_add_intrinsic_ss_code (gfc_loopinfo * loop ATTRIBUTE_UNUSED, gfc_ss * ss) +{ + switch (ss->expr->value.function.isym->generic_id) + { + case GFC_ISYM_UBOUND: + case GFC_ISYM_LBOUND: + break; + + default: + abort (); + break; + } +} + + +/* UBOUND and LBOUND intrinsics with one parameter are expanded into code + inside the scalarization loop. */ + +static gfc_ss * +gfc_walk_intrinsic_bound (gfc_ss * ss, gfc_expr * expr) +{ + gfc_ss *newss; + + /* The two argument version returns a scalar. */ + if (expr->value.function.actual->next->expr) + return ss; + + newss = gfc_get_ss (); + newss->type = GFC_SS_INTRINSIC; + newss->expr = expr; + newss->next = ss; + + return newss; +} + + +/* Walk an intrinsic array libcall. */ + +static gfc_ss * +gfc_walk_intrinsic_libfunc (gfc_ss * ss, gfc_expr * expr) +{ + gfc_ss *newss; + + assert (expr->rank > 0); + + newss = gfc_get_ss (); + newss->type = GFC_SS_FUNCTION; + newss->expr = expr; + newss->next = ss; + newss->data.info.dimen = expr->rank; + + return newss; +} + + +/* Returns nonzero if the specified intrinsic function call maps directly to a + an external library call. Should only be used for functions that return + arrays. */ + +int +gfc_is_intrinsic_libcall (gfc_expr * expr) +{ + assert (expr->expr_type == EXPR_FUNCTION && expr->value.function.isym); + assert (expr->rank > 0); + + switch (expr->value.function.isym->generic_id) + { + case GFC_ISYM_ALL: + case GFC_ISYM_ANY: + case GFC_ISYM_COUNT: + case GFC_ISYM_MATMUL: + case GFC_ISYM_MAXLOC: + case GFC_ISYM_MAXVAL: + case GFC_ISYM_MINLOC: + case GFC_ISYM_MINVAL: + case GFC_ISYM_PRODUCT: + case GFC_ISYM_SUM: + case GFC_ISYM_SHAPE: + case GFC_ISYM_SPREAD: + case GFC_ISYM_TRANSPOSE: + /* Ignore absent optional parameters. */ + return 1; + + case GFC_ISYM_RESHAPE: + case GFC_ISYM_CSHIFT: + case GFC_ISYM_EOSHIFT: + case GFC_ISYM_PACK: + case GFC_ISYM_UNPACK: + /* Pass absent optional parameters. */ + return 2; + + default: + return 0; + } +} + +/* Walk an intrinsic function. */ +gfc_ss * +gfc_walk_intrinsic_function (gfc_ss * ss, gfc_expr * expr, + gfc_intrinsic_sym * isym) +{ + assert (isym); + + if (isym->elemental) + return gfc_walk_elemental_function_args (ss, expr, GFC_SS_SCALAR); + + if (expr->rank == 0) + return ss; + + if (gfc_is_intrinsic_libcall (expr)) + return gfc_walk_intrinsic_libfunc (ss, expr); + + /* Special cases. */ + switch (isym->generic_id) + { + case GFC_ISYM_LBOUND: + case GFC_ISYM_UBOUND: + return gfc_walk_intrinsic_bound (ss, expr); + + default: + /* This probably meant someone forgot to add an intrinsic to the above + list(s) when they implemented it, or something's gone horribly wrong. + */ + gfc_todo_error ("Scalarization of non-elemental intrinsic: %s", + expr->value.function.name); + } +} + +#include "gt-fortran-trans-intrinsic.h" |