Hi All, This is very much an early version of the F2018 REDUCE intrinsic. I am posting it now because I have totally forgotten how to include new functions in libgfortran.so. -static-libfortran works fine and the results are the same as the other brands.
At present, it produces several of link warnings.
test_reduce.f90:23:2: warning: type of ‘_gfortran_reduce_scalar’ does not
match original declaration [-Wlto-type-mismatch]
23 | pure function add(i,j) result(sum_ij)
| ^
test_reduce.f90:23:2: note: return value type mismatch
test_reduce.f90:23:2: note: type ‘struct s’ should match type ‘int’
test_reduce.f90:23:2: note: ‘_gfortran_reduce_scalar’ was previously
declared here
test_reduce.f90:23:2: note: code may be misoptimized unless
‘-fno-strict-aliasing’ is used
/usr/bin/ld: warning: /tmp/ccfEUYXA.ltrans0.ltrans.o: requires executable
stack (because the .note.GNU-stack section is executable)
The last one is unavoidable because of the use of the wrapper for
'operation' that allows type agnostic use of pointer arithmetic in the
library functions. I am working on the type mismatch, which occurs when
different wrapper types appear in the same namespace.
Clearly there is a fair amount to do: clear the commented out
sections/lines, testcases and documentation.
Paul
diff --git a/gcc/fortran/check.cc b/gcc/fortran/check.cc
index 35458643835..ad617e177fb 100644
--- a/gcc/fortran/check.cc
+++ b/gcc/fortran/check.cc
@@ -5135,6 +5135,227 @@ gfc_check_real (gfc_expr *a, gfc_expr *kind)
}
+bool
+gfc_check_reduce (gfc_expr *array, gfc_expr *operation, gfc_expr *dim,
+ gfc_expr *mask, gfc_expr *identity, gfc_expr *ordered)
+{
+ symbol_attribute attr;
+ gfc_formal_arglist *formal;
+ gfc_symbol *sym;
+
+ if (array->ts.type == BT_CLASS)
+ {
+ gfc_error ("The ARRAY argument at %L of REDUCE shall not be polymorphic",
+ &array->where);
+ return false;
+ }
+
+ if (!gfc_resolve_expr (operation))
+ return false;
+
+ attr = gfc_expr_attr (operation);
+ if (!attr.pure || !attr.function)
+ {
+ gfc_error ("OPERATION argument at %L must be a PURE function",
+ &operation->where);
+ return false;
+ }
+
+ if (attr.dimension)
+ {
+ gfc_error ("OPERATION argument at %L must be a SCALAR function",
+ &operation->where);
+ return false;
+ }
+
+ if (attr.intrinsic)
+ {
+ /* None of the intrinsics fulfills the criteria of taking two arguments,
+ returning the same type and kind as the arguments and being permitted
+ as actual argument. */
+ gfc_error ("Intrinsic function %s at %L is not permitted for REDUCE",
+ operation->symtree->n.sym->name, &operation->where);
+ return false;
+ }
+
+ if (gfc_is_proc_ptr_comp (operation))
+ {
+ gfc_component *comp = gfc_get_proc_ptr_comp (operation);
+ sym = comp->ts.interface;
+ }
+ else
+ sym = operation->symtree->n.sym;
+
+ formal = sym->formal;
+
+ if (!formal || !formal->next || formal->next->next)
+ {
+ gfc_error ("The function passed as OPERATION at %L shall have two "
+ "arguments", &operation->where);
+ return false;
+ }
+
+ if (sym->result->ts.type == BT_UNKNOWN)
+ gfc_set_default_type (sym->result, 0, NULL);
+
+ if (!gfc_compare_types (&array->ts, &sym->result->ts))
+ {
+ gfc_error ("The ARRAY argument at %L has type %s but the function passed as "
+ "OPERATION at %L returns %s",
+ &array->where, gfc_typename (array), &operation->where,
+ gfc_typename (&sym->result->ts));
+ return false;
+ }
+
+ if (!gfc_compare_types (&array->ts, &formal->sym->ts)
+ || !gfc_compare_types (&array->ts, &formal->next->sym->ts))
+ {
+ gfc_error ("The function passed as OPERATION at %L has arguments of type "
+ "%s and %s but shall have type %s", &operation->where,
+ gfc_typename (&formal->sym->ts),
+ gfc_typename (&formal->next->sym->ts), gfc_typename (array));
+ return false;
+ }
+
+ if (operation->rank || attr.allocatable || attr.pointer)
+ {
+ gfc_error ("The function passed as OPERATION at %L shall be scalar "
+ "non-allocatable and non-pointer", &operation->where);
+ return false;
+ }
+
+ if (formal->sym->attr.dimension || formal->next->sym->attr.dimension)
+ {
+ gfc_error ("The function passed as OPERATION at %L shall have scalar "
+ "arguments and return a scalar", &operation->where);
+ return false;
+ }
+
+ if (formal->sym->attr.allocatable || formal->sym->attr.allocatable
+ || formal->sym->attr.pointer || formal->sym->attr.pointer
+ || formal->sym->attr.optional || formal->sym->attr.optional
+ || formal->sym->ts.type == BT_CLASS || formal->sym->ts.type == BT_CLASS)
+ {
+ gfc_error ("Each argument of OPERATION at %L shall be a scalar, "
+ "non-allocatable, non-pointer, non-polymorphic and "
+ "nonoptional", &operation->where);
+ return false;
+ }
+
+ if (formal->sym->attr.value != formal->next->sym->attr.value)
+ {
+ gfc_error ("The function passed as OPERATION at %L shall have the VALUE "
+ "attribute either for none or both arguments",
+ &operation->where);
+ return false;
+ }
+
+ if (formal->sym->attr.target != formal->next->sym->attr.target)
+ {
+ gfc_error ("The function passed as OPERATION at %L shall have the TARGET "
+ "attribute either for none or both arguments",
+ &operation->where);
+ return false;
+ }
+
+ if (formal->sym->attr.asynchronous != formal->next->sym->attr.asynchronous)
+ {
+ gfc_error ("The function passed as OPERATION at %L shall have the "
+ "ASYNCHRONOUS attribute either for none or both arguments",
+ &operation->where);
+ return false;
+ }
+
+ if (formal->sym->attr.optional || formal->next->sym->attr.optional)
+ {
+ gfc_error ("The function passed as OPERATION at %L shall not have the "
+ "OPTIONAL attribute for either of the arguments",
+ &operation->where);
+ return false;
+ }
+
+ if (array->ts.type == BT_CHARACTER)
+ {
+ gfc_charlen *cl;
+ unsigned long actual_size, formal_size1, formal_size2, result_size;
+
+ cl = array->ts.u.cl;
+ actual_size = cl && cl->length && cl->length->expr_type == EXPR_CONSTANT
+ ? mpz_get_ui (cl->length->value.integer) : 0;
+
+ cl = formal->sym->ts.u.cl;
+ formal_size1 = cl && cl->length && cl->length->expr_type == EXPR_CONSTANT
+ ? mpz_get_ui (cl->length->value.integer) : 0;
+
+ cl = formal->next->sym->ts.u.cl;
+ formal_size2 = cl && cl->length && cl->length->expr_type == EXPR_CONSTANT
+ ? mpz_get_ui (cl->length->value.integer) : 0;
+
+ cl = sym->ts.u.cl;
+ result_size = cl && cl->length && cl->length->expr_type == EXPR_CONSTANT
+ ? mpz_get_ui (cl->length->value.integer) : 0;
+
+ if (actual_size
+ && ((formal_size1 && actual_size != formal_size1)
+ || (formal_size2 && actual_size != formal_size2)))
+ {
+ gfc_error ("The character length of the ARRAY argument at %L and of the "
+ "arguments of the OPERATION at %L shall be the same",
+ &array->where, &operation->where);
+ return false;
+ }
+
+ if (actual_size && result_size && actual_size != result_size)
+ {
+ gfc_error ("The character length of the ARRAY argument at %L and of the "
+ "function result of the OPERATION at %L shall be the same",
+ &array->where, &operation->where);
+ return false;
+ }
+ }
+
+ if (dim && (dim->rank || dim->ts.type != BT_INTEGER))
+ {
+ gfc_error ("The DIM argument at %L, if present, must be an integer scalar",
+ &dim->where);
+ return false;
+ }
+
+ if (mask && (array->rank != mask->rank || mask->ts.type != BT_LOGICAL))
+ {
+ gfc_error ("The MASK argument at %L, if present, must be a logical "
+ "array with the same rank as ARRAY", &mask->where);
+ return false;
+ }
+
+ if (mask
+ && !gfc_check_conformance (array, mask,
+ _("arguments '%s' and '%s' for intrinsic %s"),
+ "ARRAY", "MASK", "REDUCE"))
+ return false;
+
+ if (mask && !identity)
+ gfc_warning (0, "MASK present at %L without INDENTITY", &mask->where);
+
+ if (ordered && (ordered->rank || ordered->ts.type != BT_LOGICAL))
+ {
+ gfc_error ("The ORDERED argument at %L, if present, must be a logical "
+ "scalar", &ordered->where);
+ return false;
+ }
+
+ if (identity && (identity->rank
+ || !gfc_compare_types (&array->ts, &identity->ts)))
+ {
+ gfc_error ("The IDENTITY argument at %L, if present, must be a scalar with "
+ "the same type as ARRAY", &identity->where);
+ return false;
+ }
+
+ return true;
+}
+
+
bool
gfc_check_rename (gfc_expr *path1, gfc_expr *path2)
{
diff --git a/gcc/fortran/gfortran.h b/gcc/fortran/gfortran.h
index 5fe12764615..7df97e012c7 100644
--- a/gcc/fortran/gfortran.h
+++ b/gcc/fortran/gfortran.h
@@ -645,6 +645,7 @@ enum gfc_isym_id
GFC_ISYM_RANK,
GFC_ISYM_REAL,
GFC_ISYM_REALPART,
+ GFC_ISYM_REDUCE,
GFC_ISYM_RENAME,
GFC_ISYM_REPEAT,
GFC_ISYM_RESHAPE,
@@ -2519,6 +2520,8 @@ typedef union
struct gfc_expr *);
bool (*f5)(struct gfc_expr *, struct gfc_expr *, struct gfc_expr *,
struct gfc_expr *, struct gfc_expr *);
+ bool (*f6)(struct gfc_expr *, struct gfc_expr *, struct gfc_expr *,
+ struct gfc_expr *, struct gfc_expr *, struct gfc_expr *);
}
gfc_check_f;
diff --git a/gcc/fortran/intrinsic.cc b/gcc/fortran/intrinsic.cc
index dc60d98d51b..c561797d790 100644
--- a/gcc/fortran/intrinsic.cc
+++ b/gcc/fortran/intrinsic.cc
@@ -331,7 +331,7 @@ do_ts29113_check (gfc_intrinsic_sym *specific, gfc_actual_arglist *arg)
static bool
do_check (gfc_intrinsic_sym *specific, gfc_actual_arglist *arg)
{
- gfc_expr *a1, *a2, *a3, *a4, *a5;
+ gfc_expr *a1, *a2, *a3, *a4, *a5, *a6;
if (arg == NULL)
return (*specific->check.f0) ();
@@ -361,6 +361,11 @@ do_check (gfc_intrinsic_sym *specific, gfc_actual_arglist *arg)
if (arg == NULL)
return (*specific->check.f5) (a1, a2, a3, a4, a5);
+ a6 = arg->expr;
+ arg = arg->next;
+ if (arg == NULL)
+ return (*specific->check.f6) (a1, a2, a3, a4, a5, a6);
+
gfc_internal_error ("do_check(): too many args");
}
@@ -838,6 +843,44 @@ add_sym_6fl (const char *name, gfc_isym_id id, enum klass cl, int actual_ok,
}
+/* Add a symbol to the function list where the function takes
+ 6 arguments. */
+
+static void
+add_sym_6 (const char *name, gfc_isym_id id, enum klass cl, int actual_ok, bt type,
+ int kind, int standard,
+ bool (*check) (gfc_expr *, gfc_expr *, gfc_expr *,
+ gfc_expr *, gfc_expr *, gfc_expr *),
+ gfc_expr *(*simplify) (gfc_expr *, gfc_expr *, gfc_expr *,
+ gfc_expr *, gfc_expr *, gfc_expr *),
+ void (*resolve) (gfc_expr *, gfc_expr *, gfc_expr *, gfc_expr *,
+ gfc_expr *, gfc_expr *, gfc_expr *),
+ const char *a1, bt type1, int kind1, int optional1,
+ const char *a2, bt type2, int kind2, int optional2,
+ const char *a3, bt type3, int kind3, int optional3,
+ const char *a4, bt type4, int kind4, int optional4,
+ const char *a5, bt type5, int kind5, int optional5,
+ const char *a6, bt type6, int kind6, int optional6)
+{
+ gfc_check_f cf;
+ gfc_simplify_f sf;
+ gfc_resolve_f rf;
+
+ cf.f6 = check;
+ sf.f6 = simplify;
+ rf.f6 = resolve;
+
+ add_sym (name, id, cl, actual_ok, type, kind, standard, cf, sf, rf,
+ a1, type1, kind1, optional1, INTENT_IN,
+ a2, type2, kind2, optional2, INTENT_IN,
+ a3, type3, kind3, optional3, INTENT_IN,
+ a4, type4, kind4, optional4, INTENT_IN,
+ a5, type5, kind5, optional5, INTENT_IN,
+ a6, type6, kind6, optional6, INTENT_IN,
+ (void *) 0);
+}
+
+
/* MINVAL, MAXVAL, PRODUCT, and SUM also get special treatment because
their argument also might have to be reordered. */
@@ -1358,13 +1401,13 @@ add_functions (void)
*c_ptr_2 = "c_ptr_2", *ca = "coarray", *com = "command",
*dist = "distance", *dm = "dim", *f = "field", *failed="failed",
*fs = "fsource", *han = "handler", *i = "i",
- *image = "image", *j = "j", *kind = "kind",
+ *idy = "identity", *image = "image", *j = "j", *kind = "kind",
*l = "l", *ln = "len", *level = "level", *m = "matrix", *ma = "matrix_a",
*mb = "matrix_b", *md = "mode", *mo = "mold", *msk = "mask",
*n = "n", *ncopies= "ncopies", *nm = "name", *num = "number",
- *ord = "order", *p = "p", *p1 = "path1", *p2 = "path2",
- *pad = "pad", *pid = "pid", *pos = "pos", *pt = "pointer",
- *r = "r", *rd = "round",
+ *op = "operation", *ord = "order", *odd = "ordered", *p = "p",
+ *p1 = "path1", *p2 = "path2", *pad = "pad", *pid = "pid", *pos = "pos",
+ *pt = "pointer", *r = "r", *rd = "round",
*s = "s", *set = "set", *sh = "shift", *shp = "shape",
*sig = "sig", *src = "source", *ssg = "substring",
*sta = "string_a", *stb = "string_b", *stg = "string",
@@ -2936,6 +2979,17 @@ add_functions (void)
make_generic ("sngl", GFC_ISYM_SNGL, GFC_STD_F77);
+ add_sym_6 ("reduce", GFC_ISYM_REDUCE, CLASS_TRANSFORMATIONAL, ACTUAL_NO, BT_REAL, dr, GFC_STD_F2018,
+ gfc_check_reduce, NULL, gfc_resolve_reduce,
+ ar, BT_REAL, dr, REQUIRED,
+ op, BT_REAL, dr, REQUIRED,
+ dm, BT_INTEGER, di, OPTIONAL,
+ msk, BT_LOGICAL, dl, OPTIONAL,
+ idy, BT_REAL, dr, OPTIONAL,
+ odd, BT_LOGICAL, dl, OPTIONAL);
+
+ make_generic ("reduce", GFC_ISYM_REDUCE, GFC_STD_F2018);
+
add_sym_2 ("rename", GFC_ISYM_RENAME, CLASS_IMPURE, ACTUAL_NO, BT_INTEGER, di,
GFC_STD_GNU, gfc_check_rename, NULL, gfc_resolve_rename,
p1, BT_CHARACTER, dc, REQUIRED, p2, BT_CHARACTER, dc, REQUIRED);
diff --git a/gcc/fortran/intrinsic.h b/gcc/fortran/intrinsic.h
index 34a0248adbd..fec1c24a099 100644
--- a/gcc/fortran/intrinsic.h
+++ b/gcc/fortran/intrinsic.h
@@ -144,6 +144,8 @@ bool gfc_check_rand (gfc_expr *);
bool gfc_check_range (gfc_expr *);
bool gfc_check_rank (gfc_expr *);
bool gfc_check_real (gfc_expr *, gfc_expr *);
+bool gfc_check_reduce (gfc_expr *, gfc_expr *, gfc_expr *, gfc_expr *,
+ gfc_expr *, gfc_expr *);
bool gfc_check_rename (gfc_expr *, gfc_expr *);
bool gfc_check_repeat (gfc_expr *, gfc_expr *);
bool gfc_check_reshape (gfc_expr *, gfc_expr *, gfc_expr *, gfc_expr *);
@@ -589,6 +591,8 @@ void gfc_resolve_parity (gfc_expr *, gfc_expr *, gfc_expr *);
void gfc_resolve_product (gfc_expr *, gfc_expr *, gfc_expr *, gfc_expr *);
void gfc_resolve_real (gfc_expr *, gfc_expr *, gfc_expr *);
void gfc_resolve_realpart (gfc_expr *, gfc_expr *);
+void gfc_resolve_reduce (gfc_expr *, gfc_expr *, gfc_expr *, gfc_expr *,
+ gfc_expr *, gfc_expr *, gfc_expr *);
void gfc_resolve_rename (gfc_expr *, gfc_expr *, gfc_expr *);
void gfc_resolve_repeat (gfc_expr *, gfc_expr *, gfc_expr *);
void gfc_resolve_reshape (gfc_expr *, gfc_expr *, gfc_expr *, gfc_expr *,
diff --git a/gcc/fortran/iresolve.cc b/gcc/fortran/iresolve.cc
index 55f7e1911ec..5671cdc7781 100644
--- a/gcc/fortran/iresolve.cc
+++ b/gcc/fortran/iresolve.cc
@@ -2408,6 +2408,182 @@ gfc_resolve_realpart (gfc_expr *f, gfc_expr *a)
}
+/* Generate a wrapper subroutine for the operation so that the library REDUCE
+ function can use pointer arithmetic for OPERATION and not be dependent on
+ knowledge of its type. */
+static gfc_symtree *
+generate_reduce_op_wrapper (gfc_expr *op)
+{
+ gfc_symbol *operation = op->symtree->n.sym;
+ gfc_symbol *wrapper, *a, *b, *c;
+ gfc_symtree *st;
+ char tname[GFC_MAX_SYMBOL_LEN+1];
+ char *name;
+ gfc_namespace *ns;
+// gfc_gsymbol *gsym = NULL;
+
+ /* Find the top-level namespace. */
+ for (ns = gfc_current_ns; ns; ns = ns->parent)
+ if (!ns->parent)
+ break;
+
+ sprintf (tname, "%s_%s", operation->name,
+ ns->proc_name ? ns->proc_name->name : "noname");
+ name = xasprintf ("__reduce_wrapper_%s", tname);
+
+ gfc_find_sym_tree (name, ns, 0, &st);
+
+ if (st && !strcmp (name, st->name))
+ {
+ free(name);
+ return st;
+ }
+
+ /* Create the wrapper namespace and contain it in 'ns'. */
+ gfc_namespace *sub_ns = gfc_get_namespace (ns, 0);
+ sub_ns->sibling = ns->contained;
+ ns->contained = sub_ns;
+ sub_ns->resolved = 1;
+
+ /* Set up procedure symbol. */
+ gfc_get_symbol (name, ns, &wrapper);
+ sub_ns->proc_name = wrapper;
+ wrapper->attr.flavor = FL_PROCEDURE;
+ wrapper->attr.subroutine = 1;
+ wrapper->attr.artificial = 1;
+ wrapper->attr.if_source = IFSRC_DECL;
+ if (ns->proc_name->attr.flavor == FL_MODULE)
+ wrapper->module = ns->proc_name->name;
+ gfc_set_sym_referenced (wrapper);
+
+ /* Set up formal argument for the argument 'a'. */
+ gfc_get_symbol ("a", sub_ns, &a);
+ a->ts = operation->ts;
+ a->attr.flavor = FL_VARIABLE;
+ a->attr.dummy = 1;
+ a->attr.artificial = 1;
+ wrapper->formal = gfc_get_formal_arglist ();
+ wrapper->formal->sym = a;
+ gfc_set_sym_referenced (a);
+
+ /* Set up formal argument for the argument 'b'. */
+ gfc_get_symbol ("b", sub_ns, &b);
+ b->ts = operation->ts;
+ b->attr.flavor = FL_VARIABLE;
+ b->attr.dummy = 1;
+ b->attr.artificial = 1;
+ wrapper->formal->next = gfc_get_formal_arglist ();
+ wrapper->formal->next->sym = b;
+ gfc_set_sym_referenced (b);
+
+ /* Set up formal argument for the argument 'c'. */
+ gfc_get_symbol ("c", sub_ns, &c);
+ c->ts = operation->ts;
+ c->attr.flavor = FL_VARIABLE;
+ c->attr.dummy = 1;
+ c->attr.artificial = 1;
+ wrapper->formal->next->next = gfc_get_formal_arglist ();
+ wrapper->formal->next->next->sym = c;
+ gfc_set_sym_referenced (c);
+
+ /* The only code is the assignment c = operation (a, b). */
+ sub_ns->code = gfc_get_code (EXEC_ASSIGN);
+ sub_ns->code->expr1 = gfc_lval_expr_from_sym (c);
+ sub_ns->code->expr2 = gfc_get_expr ();
+ sub_ns->code->expr2->expr_type = EXPR_FUNCTION;
+ sub_ns->code->expr2->where = gfc_current_locus;
+ sub_ns->code->expr2->ts = operation->ts;
+ gfc_get_sym_tree (operation->name, ns, &sub_ns->code->expr2->symtree, false);
+ sub_ns->code->expr2->value.function.esym = sub_ns->code->expr2->symtree->n.sym;
+ sub_ns->code->expr2->value.function.actual = gfc_get_actual_arglist ();
+ sub_ns->code->expr2->value.function.actual->expr
+ = gfc_lval_expr_from_sym (a);
+ sub_ns->code->expr2->value.function.actual->next = gfc_get_actual_arglist ();
+ sub_ns->code->expr2->value.function.actual->next->expr
+ = gfc_lval_expr_from_sym (b);
+
+ /* It is unexpected to have some symbols added at resolution. Commit the
+ changes in order to keep a clean state. */
+ gfc_commit_symbol (wrapper);
+ gfc_commit_symbol (a);
+ gfc_commit_symbol (b);
+ gfc_commit_symbol (c);
+
+ gfc_find_sym_tree (name, ns, 0, &st);
+ free(name);
+
+ return st;
+}
+
+void
+gfc_resolve_reduce (gfc_expr *f, gfc_expr *array,
+ gfc_expr *operation,
+ gfc_expr *dim,
+ gfc_expr *mask,
+ gfc_expr *identity ATTRIBUTE_UNUSED,
+ gfc_expr *ordered ATTRIBUTE_UNUSED)
+{
+ gfc_symtree *wrapper_symtree;
+ gfc_typespec ts;
+
+ gfc_resolve_expr (array);
+ if (array->ts.type == BT_CHARACTER && array->ref)
+ gfc_resolve_substring_charlen (array);
+
+ f->ts = array->ts;
+
+ /* Replace 'operation' with its subroutine wrapper so that pointers may be
+ used throughout the library function. */
+ wrapper_symtree = generate_reduce_op_wrapper (operation);
+ gcc_assert (wrapper_symtree && wrapper_symtree->n.sym);
+ operation->symtree = wrapper_symtree;
+ operation->ts = operation->symtree->n.sym->ts;
+
+ /* The scalar library function converts the scalar result to a dimension
+ zero descriptor and then returns the data after the call. */
+ if (f->ts.type == BT_CHARACTER)
+ {
+ if (dim && array->rank > 1)
+ {
+ f->value.function.name = gfc_get_string (PREFIX ("reduce_c"));
+ f->rank = array->rank - 1;
+ }
+ else
+ {
+ f->value.function.name = gfc_get_string (PREFIX ("reduce_scalar_c"));
+ f->rank = 0;
+ }
+ }
+ else
+ {
+ if (dim && array->rank > 1)
+ {
+ f->value.function.name = gfc_get_string (PREFIX ("reduce"));
+ f->rank = array->rank - 1;
+ }
+ else
+ {
+ f->value.function.name = gfc_get_string (PREFIX ("reduce_scalar"));
+ f->rank = 0;
+ }
+ }
+
+ if (dim)
+ {
+ ts = dim->ts;
+ ts.kind = gfc_default_integer_kind;
+ gfc_convert_type_warn (dim, &ts, 1, 0);
+ }
+
+ if (mask)
+ {
+ ts = mask->ts;
+ ts.kind = 4;
+ gfc_convert_type_warn (mask, &ts, 1, 0);
+ }
+}
+
+
void
gfc_resolve_rename (gfc_expr *f, gfc_expr *p1 ATTRIBUTE_UNUSED,
gfc_expr *p2 ATTRIBUTE_UNUSED)
diff --git a/gcc/fortran/trans-expr.cc b/gcc/fortran/trans-expr.cc
index f923aeb9460..cf2149eb62b 100644
--- a/gcc/fortran/trans-expr.cc
+++ b/gcc/fortran/trans-expr.cc
@@ -8389,6 +8389,12 @@ gfc_conv_procedure_call (gfc_se * se, gfc_symbol * sym,
byref = (comp && (comp->attr.dimension
|| (comp->ts.type == BT_CHARACTER && !sym->attr.is_bind_c)))
|| (!comp && gfc_return_by_reference (sym));
+
+ /* In order that the library function for intrinsic REDUCE be type and kind
+ agnostic, the result is passed by reference. Allocatable components are
+ handled within the OPERATION wrapper. */
+ bool scalar_reduce = expr && !expr->rank && expr->value.function.isym
+ && expr->value.function.isym->id == GFC_ISYM_REDUCE;
if (byref)
{
if (se->direct_byref)
@@ -8573,6 +8579,16 @@ gfc_conv_procedure_call (gfc_se * se, gfc_symbol * sym,
else if (ts.type == BT_CHARACTER)
vec_safe_push (retargs, len);
}
+ else if (scalar_reduce)
+ {
+ type = gfc_typenode_for_spec (&expr->ts);
+ tmp = gfc_create_var (type, "sr");
+ se->expr = tmp;
+ result = tmp;
+ tmp = gfc_build_addr_expr (pvoid_type_node, tmp);
+ vec_safe_push (retargs, tmp);
+ }
+
gfc_free_interface_mapping (&mapping);
/* We need to glom RETARGS + ARGLIST + STRINGARGS + APPEND_ARGS. */
@@ -8785,6 +8801,12 @@ gfc_conv_procedure_call (gfc_se * se, gfc_symbol * sym,
gfc_add_expr_to_block (&se->pre, tmp);
}
}
+ else if (scalar_reduce)
+ {
+ gfc_add_expr_to_block (&se->pre, se->expr);
+ se->expr = result;
+// gfc_add_block_to_block (&se->post, &post);
+ }
else
{
/* For a function with a class array result, save the result as
diff --git a/gcc/fortran/trans-intrinsic.cc b/gcc/fortran/trans-intrinsic.cc
index 51237d0d3be..d3e6d626afa 100644
--- a/gcc/fortran/trans-intrinsic.cc
+++ b/gcc/fortran/trans-intrinsic.cc
@@ -4250,6 +4250,20 @@ gfc_get_symbol_for_expr (gfc_expr * expr, bool ignore_optional)
sym->attr.proc = PROC_INTRINSIC;
sym->attr.flavor = FL_PROCEDURE;
sym->result = sym;
+#if 0
+ if (expr->value.function.isym
+ && expr->value.function.isym->id == GFC_ISYM_REDUCE)
+ {
+ if (expr->value.function.actual
+ && expr->value.function.actual->next
+ && expr->value.function.actual->next->next
+ && expr->value.function.actual->next->next->expr == NULL)
+ expr->rank = 0;
+ else if (expr->value.function.actual
+ && expr->value.function.actual->expr)
+ expr->rank = expr->value.function.actual->expr->rank - 1;
+ }
+#endif
if (expr->rank > 0)
{
sym->attr.dimension = 1;
@@ -9941,6 +9955,16 @@ gfc_conv_intrinsic_sr_kind (gfc_se *se, gfc_expr *expr)
}
+/* Generate code for REDUCE function with a scalar result. This is handled as a
+ special case within gfc_conv_procedure call. */
+static void
+gfc_conv_intrinsic_scalar_reduce (gfc_se *se, gfc_expr *expr)
+{
+ gcc_assert (expr->rank == 0);
+ gfc_conv_intrinsic_funcall (se, expr);
+}
+
+
/* Generate code for TRIM (A) intrinsic function. */
static void
@@ -11451,6 +11475,7 @@ gfc_conv_intrinsic_function (gfc_se * se, gfc_expr * expr)
case GFC_ISYM_EOSHIFT:
case GFC_ISYM_PACK:
case GFC_ISYM_RESHAPE:
+ case GFC_ISYM_REDUCE:
/* For all of those the first argument specifies the type and the
third is optional. */
conv_generic_with_optional_char_arg (se, expr, 1, 3);
@@ -11482,6 +11507,10 @@ gfc_conv_intrinsic_function (gfc_se * se, gfc_expr * expr)
case GFC_ISYM_NONE:
gcc_unreachable ();
+ case GFC_ISYM_REDUCE:
+ gfc_conv_intrinsic_scalar_reduce (se, expr);
+ break;
+
case GFC_ISYM_REPEAT:
gfc_conv_intrinsic_repeat (se, expr);
break;
@@ -12575,6 +12604,7 @@ gfc_is_intrinsic_libcall (gfc_expr * expr)
case GFC_ISYM_FAILED_IMAGES:
case GFC_ISYM_STOPPED_IMAGES:
case GFC_ISYM_PACK:
+ case GFC_ISYM_REDUCE:
case GFC_ISYM_RESHAPE:
case GFC_ISYM_UNPACK:
/* Pass absent optional parameters. */
diff --git a/libgfortran/Makefile.am b/libgfortran/Makefile.am
index 52bd6ea641f..21b35c76a06 100644
--- a/libgfortran/Makefile.am
+++ b/libgfortran/Makefile.am
@@ -149,6 +149,7 @@ intrinsics/spread_generic.c \
intrinsics/string_intrinsics.c \
intrinsics/rand.c \
intrinsics/random.c \
+intrinsics/reduce.c \
intrinsics/reshape_generic.c \
intrinsics/reshape_packed.c \
intrinsics/selected_int_kind.f90 \
diff --git a/libgfortran/Makefile.in b/libgfortran/Makefile.in
index 9b9ed2f86ba..4f47f3e7110 100644
--- a/libgfortran/Makefile.in
+++ b/libgfortran/Makefile.in
@@ -613,6 +613,7 @@ am__objects_59 = intrinsics/associated.lo intrinsics/abort.lo \
intrinsics/selected_char_kind.lo intrinsics/size.lo \
intrinsics/spread_generic.lo intrinsics/string_intrinsics.lo \
intrinsics/rand.lo intrinsics/random.lo \
+ intrinsics/reduce.lo \
intrinsics/reshape_generic.lo intrinsics/reshape_packed.lo \
intrinsics/selected_int_kind.lo \
intrinsics/selected_real_kind.lo intrinsics/trigd.lo \
@@ -1029,6 +1030,7 @@ gfor_helper_src = intrinsics/associated.c intrinsics/abort.c \
intrinsics/selected_char_kind.c intrinsics/size.c \
intrinsics/spread_generic.c intrinsics/string_intrinsics.c \
intrinsics/rand.c intrinsics/random.c \
+ intrinsics/reduce.c \
intrinsics/reshape_generic.c intrinsics/reshape_packed.c \
intrinsics/selected_int_kind.f90 \
intrinsics/selected_real_kind.f90 intrinsics/trigd.c \
@@ -3476,6 +3478,8 @@ intrinsics/rand.lo: intrinsics/$(am__dirstamp) \
intrinsics/$(DEPDIR)/$(am__dirstamp)
intrinsics/random.lo: intrinsics/$(am__dirstamp) \
intrinsics/$(DEPDIR)/$(am__dirstamp)
+intrinsics/reduce.lo: intrinsics/$(am__dirstamp) \
+ intrinsics/$(DEPDIR)/$(am__dirstamp)
intrinsics/reshape_generic.lo: intrinsics/$(am__dirstamp) \
intrinsics/$(DEPDIR)/$(am__dirstamp)
intrinsics/reshape_packed.lo: intrinsics/$(am__dirstamp) \
diff --git a/libgfortran/gfortran.map b/libgfortran/gfortran.map
index 159a862e90a..4948e578bb5 100644
--- a/libgfortran/gfortran.map
+++ b/libgfortran/gfortran.map
@@ -2023,4 +2023,6 @@ GFORTRAN_15 {
_gfortran_pow_m16_m4;
_gfortran_pow_m16_m8;
_gfortran_pow_m16_m16;
+ _gfortran_reduce;
+ _gfortran_reduce_scalar;
} GFORTRAN_14;
diff --git a/libgfortran/intrinsics/reduce.c b/libgfortran/intrinsics/reduce.c
new file mode 100644
index 00000000000..43d945f9b49
--- /dev/null
+++ b/libgfortran/intrinsics/reduce.c
@@ -0,0 +1,266 @@
+/* Generic implementation of the reduce intrinsic
+ Copyright (C) 2002-2025 Free Software Foundation, Inc.
+ Contributed by Paul Thomas <[email protected]>
+
+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.
+
+Ligbfortran is distributed in the hope that it will be useful,
+but WITHOUT ANY WarrayANTY; without even the implied warrayanty 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 <string.h>
+#include <stdio.h>
+
+typedef GFC_FULL_ARRAY_DESCRIPTOR(GFC_MAX_DIMENSIONS, char) parray;
+
+static index_type
+product (index_type arr[], index_type start, index_type end)
+{
+ index_type i;
+ index_type prod;
+ prod = 1;
+ /* Retained fortran indexing for start and end! */
+ for (i = start - 1; i < end; i++)
+ prod = prod * arr[i];
+ return prod;
+}
+
+
+extern void reduce (parray *, parray *, void (*operator) (void *, void *, void *),
+ int *, gfc_array_l4 *, void *, void *);
+export_proto(reduce);
+
+void
+reduce (parray *ret,
+ parray *array,
+ void (*operator) (void *, void *, void *),
+ GFC_INTEGER_4 *dim,
+ gfc_array_l4 *mask,
+ void *identity,
+ void *ordered __attribute__ ((unused)))
+{
+ GFC_LOGICAL_4 maskR = 0;
+ index_type array_shape[GFC_MAX_DIMENSIONS];
+ void *ptr;
+ void *accumulator;
+ void *res;
+ index_type idx[3], idx_stride[3];
+ index_type dimen, dimen_m1, idx0, idx1, idx2, ldx;
+ bool started;
+ bool masked = false;
+ bool dim_present = dim != NULL;
+ bool mask_present = mask != NULL;
+ bool identity_present = identity != NULL;
+ int i;
+ int array_rank = (int)GFC_DESCRIPTOR_RANK (array);
+ size_t elem_len = GFC_DESCRIPTOR_SIZE (array);
+
+/* First part of the algorithm */
+ for (i = 0; i < array_rank; i++)
+ array_shape[i] = GFC_DESCRIPTOR_EXTENT (array,i);
+
+ if (dim_present)
+ dimen = (index_type) *dim;
+ else
+ dimen = 1;
+ dimen_m1 = dimen -1;
+
+ for (i = 0; i < array_rank; i++)
+ {
+ array_shape[i] = GFC_DESCRIPTOR_EXTENT (array,i);
+ if (masked && (array_shape[i] != GFC_DESCRIPTOR_EXTENT (mask, i)))
+ runtime_error ("shape mismatch between ARRAY and MASK in REDUCE "
+ "intrinsic");
+ }
+
+ /* Set up the shape and strides for the reduction. This is made relatively
+ painless by the use of pointer arithmetic throughout (except for MASK,
+ whose type is known. */
+ idx[0] = idx[1] = idx[2] = 1;
+ idx_stride[0] = idx_stride[1] = idx_stride[2] = 1;
+
+ if ((!dim_present && array_rank > 1) || array_rank == 1)
+ idx[1] = product (array_shape, 1, (index_type)array_rank);
+ else
+ {
+ idx[0] = product (array_shape, 1 , dimen_m1);
+ idx[1] = product (array_shape, dimen , dimen);
+ idx[2] = product (array_shape, (dimen + 1), (index_type)array_rank);
+
+ idx_stride[1] = GFC_DESCRIPTOR_STRIDE (array, dimen_m1);
+ if (dimen < array_rank)
+ idx_stride[2] = GFC_DESCRIPTOR_STRIDE (array, dimen);
+ else
+ idx_stride[2] = 1;
+
+ for (i = 0; i < (array_rank - 1); i++)
+ {
+ if (i < (int)(dimen - 1))
+ GFC_DIMENSION_SET(ret->dim[i], 0,
+ GFC_DESCRIPTOR_EXTENT (array,i) - 1,
+ GFC_DESCRIPTOR_STRIDE (array,i));
+ else
+ GFC_DIMENSION_SET (ret->dim[i], 0,
+ GFC_DESCRIPTOR_EXTENT (array,i + 1) - 1,
+ GFC_DESCRIPTOR_STRIDE (array,i));
+ }
+ }
+
+ /* Allocate the stash for the iterated dimension and the result data */
+ accumulator = malloc ((size_t)(elem_len * idx[1]));
+ if (ret->base_addr == NULL)
+ ret->base_addr = malloc ((size_t)(elem_len * idx[0] * idx[2]));
+
+
+ /* Do the reduction */
+ for (idx0 = 0; idx0 < idx[0]; idx0++)
+ {
+ for (idx2 = 0; idx2 < idx[2]; idx2++)
+ {
+ ldx = idx0 + idx2 * idx_stride[2];
+ if (mask_present)
+ maskR = mask->base_addr[ldx];
+
+ started = (mask_present && maskR) || !mask_present;
+
+ /* Fill the stash along the iterated dimension. */
+ for (idx1 = 0; idx1 < idx[1]; idx1++)
+ {
+ ptr = array->base_addr + (size_t)((idx0 + idx1 * idx_stride[1]
+ + idx2 * idx_stride[2]) * elem_len);
+ memcpy (accumulator + (size_t)(idx1 * elem_len), ptr, elem_len);
+ }
+
+ /* Start the iteration. */
+ for (idx1 = 1; idx1 < idx[1]; idx1++)
+ {
+ /* If masked, cycle until after first element that is not masked
+ out. Then set 'started' and cycle so that this becomes the first
+ element in the reduction. */
+ ldx = idx0 + idx1 * idx_stride[1] + idx2 * idx_stride[2];
+ if (mask_present)
+ maskR = mask->base_addr[ldx];
+
+ ptr = accumulator + (size_t)(idx1 * elem_len);
+ if (!started)
+ {
+ if (mask_present && maskR)
+ started = true;
+ continue;
+ }
+
+ /* Call the operation, if not masked out, with the previous and
+ current elements as arguments. Place the result in the
+ previous element. */
+ if ((mask_present && maskR) || !mask_present)
+ operator (ptr - elem_len, ptr, ptr - elem_len);
+
+ /* Unconditionally carry over the previous element into this one
+ so that the accumulated result is taken forward. */
+ memcpy (ptr, ptr - elem_len, elem_len);
+ }
+
+ ptr = accumulator + (size_t)((idx[1] - 1) * elem_len);
+ res = ret->base_addr + (size_t)((idx0 + idx2 * idx_stride[1]) * elem_len);
+
+ /* If this result element is empty emit an error or, if available,
+ set to identity. */
+ if (started)
+ memcpy (res, ptr, elem_len);
+ else
+ {
+ if (!identity_present)
+ runtime_error ("Empty column and no IDENTITY in REDUCE intrinsic");
+ else
+ memcpy (res, identity, elem_len);
+ }
+ }
+ }
+ free (accumulator);
+}
+
+
+extern void reduce_scalar (void *, parray *, void (*operator) (void *, void *, void *),
+ int *, gfc_array_l4 *, void *, void *);
+export_proto(reduce_scalar);
+
+void
+reduce_scalar (void *res,
+ parray *array,
+ void (*operator) (void *, void *, void *),
+ int *dim,
+ gfc_array_l4 *mask,
+ void *identity,
+ void *ordered)
+{
+ parray ret;
+ ret.base_addr = NULL;
+ ret.dtype.rank = 0;
+ reduce (&ret, array, operator, dim, mask, identity, ordered);
+ memcpy (res, ret.base_addr, GFC_DESCRIPTOR_SIZE(array));
+}
+
+extern void reduce_c (parray *, index_type, parray *,
+ void (*operator) (void *, void *, void *),
+ int *, gfc_array_l4 *, void *, void *,
+ index_type, index_type);
+export_proto(reduce_c);
+
+void
+reduce_c (parray *ret,
+ index_type ret_strlen,
+ parray *array,
+ void (*operator) (void *, void *, void *),
+ GFC_INTEGER_4 *dim,
+ gfc_array_l4 *mask,
+ void *identity,
+ void *ordered,
+ index_type array_strlen __attribute__ ((unused)),
+ index_type identity_strlen __attribute__ ((unused)))
+{
+ reduce (ret, array, operator, dim, mask, identity, ordered);
+}
+
+
+extern void reduce_scalar_c (void *, index_type, parray *,
+ void (*operator) (void *, void *, void *),
+ int *, gfc_array_l4 *, void *, void *,
+ index_type, index_type);
+export_proto(reduce_scalar_c);
+
+
+void
+reduce_scalar_c (void *res,
+ index_type res_strlen,
+ parray *array,
+ void (*operator) (void *, void *, void *),
+ int *dim,
+ gfc_array_l4 *mask,
+ void *identity,
+ void *ordered,
+ index_type array_strlen __attribute__ ((unused)),
+ index_type identity_strlen __attribute__ ((unused)))
+{
+ parray ret;
+ ret.base_addr = NULL;
+ ret.dtype.rank = 0;
+ reduce (&ret, array, operator, dim, mask, identity, ordered);
+ memcpy (res, ret.base_addr, GFC_DESCRIPTOR_SIZE(array));
+}
+
Change.Logs
Description: Binary data
program test_reduce ! intrinsic reduce works with nagfor and Intel Fortran, not yet gfortran
implicit none
integer, parameter :: n = 3, vec(n) = [2, 5, 10], &
mat(n,2) = reshape([vec,2*vec],shape=[size(vec),2])
integer, dimension(:,:), allocatable :: res2
logical, parameter :: t = .true., f = .false., keep(size(vec)) = [t,f,t]
logical, parameter :: keepM(n,2) = reshape([keep,keep],shape=[n,2])
integer :: i
print *, "my reduce ", reduce (vec, add, dim=1), "should be 17"
print *, "my reduce ", reduce (vec, mult, 1), "should be 100"
print *, "my reduce ", reduce (mat, add, 1), "should be 17 34"
print *, "my reduce ", reduce (mat, mult, 1), "should be 100 800"
print *, "my reduce ", reduce (mat, add, 2), "should be 6 15 30"
print *, "my reduce ", reduce (mat, mult, 2), "should be 8 50 200"
print *, "my reduce ", reduce (mat, add), "should be 51"
print *, "my reduce ", reduce (mat, mult), "should be 80000"
print *, "with mask"
print *, "my reduce ", reduce (vec,add, mask=keep), "should be 12"
print *, "my reduce ", reduce (vec,mult, mask=keep), "should be 20"
print *, "my reduce ", reduce (mat, add, mask=keepM), "should be 36"
print *, "my reduce ", reduce (mat, mult, mask=keepM), "should be 1600"
Print *, "higher dimension"
print *, "my reduce ", reduce (reshape ([(i, i=1,8)], [2,2,2]),mult), "should be 40320"
print *, "my reduce ", reduce (reshape ([(i, i=1,8)], [2,2,2]),mult,dim=2), "should be 3 8 35 48"
print *, "my reduce ", reduce (reshape ([(i, i=1,8)], [2,2,2]),mult,dim=2, &
mask=reshape ([t,f,t,f,t,f,t,f],[2,2,2]), identity=-1), "should be 3 -1 35 -1"
print *, "my reduce ", reduce (reshape([(i, i=1,16)], [2,4,2]),add, dim = 3, &
mask=reshape([f,t,t,f,t,t,t,t,t,t,t,t,t,t,t,t],[2,4,2]), identity=-1, ordered = f), "should be 9 12 14 12 18 20 22 24"
print *, "my reduce ", reduce (reshape([(i, i=1,16)], [4,4]),add, dim = 2, &
mask=reshape([f,t,t,f,t,t,t,t,t,t,t,t,t,t,t,t],[4,4]), identity=-1, ordered = f), "should be 27 32 36 36"
!print*,mat(:,1) ! 2 5 10
!print*,mat(:,2) ! 4 10 20
! In lines below the two expressions give the same results.
! reduce applied to vec(:)
!print*,reduce(vec,add),sum(vec) ! 17
!print*,reduce(vec,mult),product(vec) ! 100
!print*,reduce(vec,add,mask=keep),sum(vec,mask=keep) ! 12
!print*,reduce(vec,mult,mask=keep),product(vec,mask=keep) ! 20
! reduce applied to mat(:,:)
!print*,reduce(mat,add),sum(mat) ! 51
!print*,reduce(mat,mult),product(mat) ! 80000
!print*,reduce(mat,add,dim=1),sum(mat,dim=1) ! 17 34
!print*,reduce(mat,mult,dim=1),product(mat,dim=1) ! 100 800
!print*,reduce(mat,add,dim=2),sum(mat,dim=2) ! 6 15 30
!print*,reduce(mat,mult,dim=2),product(mat,dim=2) ! 8 50 200
contains
pure function add(i,j) result(sum_ij)
integer, intent(in) :: i, j
integer :: sum_ij
sum_ij = i + j
end function add
!
pure function mult(i,j) result(prod_ij)
integer, intent(in) :: i, j
integer :: prod_ij
prod_ij = i*j
end function mult
end
