summaryrefslogtreecommitdiff
path: root/gcc/fortran/openmp.cc
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/fortran/openmp.cc')
-rw-r--r--gcc/fortran/openmp.cc403
1 files changed, 403 insertions, 0 deletions
diff --git a/gcc/fortran/openmp.cc b/gcc/fortran/openmp.cc
index ce719bd5d92..653c43f79ff 100644
--- a/gcc/fortran/openmp.cc
+++ b/gcc/fortran/openmp.cc
@@ -29,6 +29,86 @@ along with GCC; see the file COPYING3. If not see
#include "diagnostic.h"
#include "gomp-constants.h"
#include "target-memory.h" /* For gfc_encode_character. */
+#include "bitmap.h"
+
+
+static gfc_statement omp_code_to_statement (gfc_code *);
+
+enum gfc_omp_directive_kind {
+ GFC_OMP_DIR_DECLARATIVE,
+ GFC_OMP_DIR_EXECUTABLE,
+ GFC_OMP_DIR_INFORMATIONAL,
+ GFC_OMP_DIR_META,
+ GFC_OMP_DIR_SUBSIDIARY,
+ GFC_OMP_DIR_UTILITY
+};
+
+struct gfc_omp_directive {
+ const char *name;
+ enum gfc_omp_directive_kind kind;
+ gfc_statement st;
+};
+
+/* Alphabetically sorted OpenMP clauses, except that longer strings are before
+ substrings; excludes combined/composite directives. See note for "ordered"
+ and "nothing". */
+
+static const struct gfc_omp_directive gfc_omp_directives[] = {
+ /* {"allocate", GFC_OMP_DIR_DECLARATIVE, ST_OMP_ALLOCATE}, */
+ /* {"allocators", GFC_OMP_DIR_EXECUTABLE, ST_OMP_ALLOCATORS}, */
+ {"assumes", GFC_OMP_DIR_INFORMATIONAL, ST_OMP_ASSUMES},
+ {"assume", GFC_OMP_DIR_INFORMATIONAL, ST_OMP_ASSUME},
+ {"atomic", GFC_OMP_DIR_EXECUTABLE, ST_OMP_ATOMIC},
+ {"barrier", GFC_OMP_DIR_EXECUTABLE, ST_OMP_BARRIER},
+ {"cancellation point", GFC_OMP_DIR_EXECUTABLE, ST_OMP_CANCELLATION_POINT},
+ {"cancel", GFC_OMP_DIR_EXECUTABLE, ST_OMP_CANCEL},
+ {"critical", GFC_OMP_DIR_EXECUTABLE, ST_OMP_CRITICAL},
+ /* {"declare mapper", GFC_OMP_DIR_DECLARATIVE, ST_OMP_DECLARE_MAPPER}, */
+ {"declare reduction", GFC_OMP_DIR_DECLARATIVE, ST_OMP_DECLARE_REDUCTION},
+ {"declare simd", GFC_OMP_DIR_DECLARATIVE, ST_OMP_DECLARE_SIMD},
+ {"declare target", GFC_OMP_DIR_DECLARATIVE, ST_OMP_DECLARE_TARGET},
+ {"declare variant", GFC_OMP_DIR_DECLARATIVE, ST_OMP_DECLARE_VARIANT},
+ {"depobj", GFC_OMP_DIR_EXECUTABLE, ST_OMP_DEPOBJ},
+ /* {"dispatch", GFC_OMP_DIR_EXECUTABLE, ST_OMP_DISPATCH}, */
+ {"distribute", GFC_OMP_DIR_EXECUTABLE, ST_OMP_DISTRIBUTE},
+ {"do", GFC_OMP_DIR_EXECUTABLE, ST_OMP_DO},
+ /* "error" becomes GFC_OMP_DIR_EXECUTABLE with at(execution) */
+ {"error", GFC_OMP_DIR_UTILITY, ST_OMP_ERROR},
+ {"flush", GFC_OMP_DIR_EXECUTABLE, ST_OMP_FLUSH},
+ /* {"interop", GFC_OMP_DIR_EXECUTABLE, ST_OMP_INTEROP}, */
+ {"loop", GFC_OMP_DIR_EXECUTABLE, ST_OMP_LOOP},
+ {"masked", GFC_OMP_DIR_EXECUTABLE, ST_OMP_MASKED},
+ /* {"metadirective", GFC_OMP_DIR_META, ST_OMP_METADIRECTIVE}, */
+ /* Note: gfc_match_omp_nothing returns ST_NONE. */
+ {"nothing", GFC_OMP_DIR_UTILITY, ST_OMP_NOTHING},
+ /* Special case; for now map to the first one.
+ ordered-blockassoc = ST_OMP_ORDERED
+ ordered-standalone = ST_OMP_ORDERED_DEPEND + depend/doacross. */
+ {"ordered", GFC_OMP_DIR_EXECUTABLE, ST_OMP_ORDERED},
+ {"parallel", GFC_OMP_DIR_EXECUTABLE, ST_OMP_PARALLEL},
+ {"requires", GFC_OMP_DIR_INFORMATIONAL, ST_OMP_REQUIRES},
+ {"scan", GFC_OMP_DIR_SUBSIDIARY, ST_OMP_SCAN},
+ {"scope", GFC_OMP_DIR_EXECUTABLE, ST_OMP_SCOPE},
+ {"sections", GFC_OMP_DIR_EXECUTABLE, ST_OMP_SECTIONS},
+ {"section", GFC_OMP_DIR_SUBSIDIARY, ST_OMP_SECTION},
+ {"simd", GFC_OMP_DIR_EXECUTABLE, ST_OMP_SIMD},
+ {"single", GFC_OMP_DIR_EXECUTABLE, ST_OMP_SINGLE},
+ {"target data", GFC_OMP_DIR_EXECUTABLE, ST_OMP_TARGET_DATA},
+ {"target enter data", GFC_OMP_DIR_EXECUTABLE, ST_OMP_TARGET_ENTER_DATA},
+ {"target exit data", GFC_OMP_DIR_EXECUTABLE, ST_OMP_TARGET_EXIT_DATA},
+ {"target update", GFC_OMP_DIR_EXECUTABLE, ST_OMP_TARGET_UPDATE},
+ {"target", GFC_OMP_DIR_EXECUTABLE, ST_OMP_TARGET},
+ {"taskloop", GFC_OMP_DIR_EXECUTABLE, ST_OMP_TASKLOOP},
+ {"taskwait", GFC_OMP_DIR_EXECUTABLE, ST_OMP_TASKWAIT},
+ {"taskyield", GFC_OMP_DIR_EXECUTABLE, ST_OMP_TASKYIELD},
+ {"task", GFC_OMP_DIR_EXECUTABLE, ST_OMP_TASK},
+ {"teams", GFC_OMP_DIR_EXECUTABLE, ST_OMP_TEAMS},
+ {"threadprivate", GFC_OMP_DIR_DECLARATIVE, ST_OMP_THREADPRIVATE},
+ /* {"tile", GFC_OMP_DIR_EXECUTABLE, ST_OMP_TILE}, */
+ /* {"unroll", GFC_OMP_DIR_EXECUTABLE, ST_OMP_UNROLL}, */
+ {"workshare", GFC_OMP_DIR_EXECUTABLE, ST_OMP_WORKSHARE},
+};
+
/* Match an end of OpenMP directive. End of OpenMP directive is optional
whitespace, followed by '\n' or comment '!'. */
@@ -111,6 +191,13 @@ gfc_free_omp_clauses (gfc_omp_clauses *c)
gfc_free_expr_list (c->wait_list);
gfc_free_expr_list (c->tile_list);
free (CONST_CAST (char *, c->critical_name));
+ if (c->assume)
+ {
+ free (c->assume->absent);
+ free (c->assume->contains);
+ gfc_free_expr_list (c->assume->holds);
+ free (c->assume);
+ }
free (c);
}
@@ -992,6 +1079,7 @@ enum omp_mask2
OMP_CLAUSE_HAS_DEVICE_ADDR, /* OpenMP 5.1 */
OMP_CLAUSE_ENTER, /* OpenMP 5.2 */
OMP_CLAUSE_DOACROSS, /* OpenMP 5.2 */
+ OMP_CLAUSE_ASSUMPTIONS, /* OpenMP 5.1. */
/* This must come last. */
OMP_MASK2_LAST
};
@@ -1407,6 +1495,173 @@ gfc_match_omp_clause_reduction (char pc, gfc_omp_clauses *c, bool openacc,
return MATCH_YES;
}
+static match
+gfc_omp_absent_contains_clause (gfc_omp_assumptions **assume, bool is_absent)
+{
+ if (*assume == NULL)
+ *assume = gfc_get_omp_assumptions ();
+ do
+ {
+ gfc_statement st = ST_NONE;
+ gfc_gobble_whitespace ();
+ locus old_loc = gfc_current_locus;
+ char c = gfc_peek_ascii_char ();
+ enum gfc_omp_directive_kind kind
+ = GFC_OMP_DIR_DECLARATIVE; /* Silence warning. */
+ for (size_t i = 0; i < ARRAY_SIZE (gfc_omp_directives); i++)
+ {
+ if (gfc_omp_directives[i].name[0] > c)
+ break;
+ if (gfc_omp_directives[i].name[0] != c)
+ continue;
+ if (gfc_match (gfc_omp_directives[i].name) == MATCH_YES)
+ {
+ st = gfc_omp_directives[i].st;
+ kind = gfc_omp_directives[i].kind;
+ }
+ }
+ gfc_gobble_whitespace ();
+ c = gfc_peek_ascii_char ();
+ if (st == ST_NONE || (c != ',' && c != ')'))
+ {
+ if (st == ST_NONE)
+ gfc_error ("Unknown directive at %L", &old_loc);
+ else
+ gfc_error ("Invalid combined or composit directive at %L",
+ &old_loc);
+ return MATCH_ERROR;
+ }
+ if (kind == GFC_OMP_DIR_DECLARATIVE
+ || kind == GFC_OMP_DIR_INFORMATIONAL
+ || kind == GFC_OMP_DIR_META)
+ {
+ gfc_error ("Invalid %qs directive at %L in %s clause: declarative, "
+ "informational and meta directives not permitted",
+ gfc_ascii_statement (st, true), &old_loc,
+ is_absent ? "ABSENT" : "CONTAINS");
+ return MATCH_ERROR;
+ }
+ if (is_absent)
+ {
+ /* Use exponential allocation; equivalent to pow2p(x). */
+ int i = (*assume)->n_absent;
+ int size = ((i == 0) ? 4
+ : pow2p_hwi (i) == 1 ? i*2 : 0);
+ if (size != 0)
+ (*assume)->absent = XRESIZEVEC (gfc_statement,
+ (*assume)->absent, size);
+ (*assume)->absent[(*assume)->n_absent++] = st;
+ }
+ else
+ {
+ int i = (*assume)->n_contains;
+ int size = ((i == 0) ? 4
+ : pow2p_hwi (i) == 1 ? i*2 : 0);
+ if (size != 0)
+ (*assume)->contains = XRESIZEVEC (gfc_statement,
+ (*assume)->contains, size);
+ (*assume)->contains[(*assume)->n_contains++] = st;
+ }
+ gfc_gobble_whitespace ();
+ if (gfc_match(",") == MATCH_YES)
+ continue;
+ if (gfc_match(")") == MATCH_YES)
+ break;
+ gfc_error ("Expected %<,%> or %<)%> at %C");
+ return MATCH_ERROR;
+ }
+ while (true);
+
+ return MATCH_YES;
+}
+
+/* Check 'check' argument for duplicated statements in absent and/or contains
+ clauses. If 'merge', merge them from check to 'merge'. */
+
+static match
+omp_verify_merge_absent_contains (gfc_statement st, gfc_omp_assumptions *check,
+ gfc_omp_assumptions *merge, locus *loc)
+{
+ if (check == NULL)
+ return MATCH_YES;
+ bitmap_head absent_head, contains_head;
+ bitmap_obstack_initialize (NULL);
+ bitmap_initialize (&absent_head, &bitmap_default_obstack);
+ bitmap_initialize (&contains_head, &bitmap_default_obstack);
+
+ match m = MATCH_YES;
+ for (int i = 0; i < check->n_absent; i++)
+ if (!bitmap_set_bit (&absent_head, check->absent[i]))
+ {
+ gfc_error ("%qs directive mentioned multiple times in %s clause in %s "
+ "directive at %L",
+ gfc_ascii_statement (check->absent[i], true),
+ "ABSENT", gfc_ascii_statement (st), loc);
+ m = MATCH_ERROR;
+ }
+ for (int i = 0; i < check->n_contains; i++)
+ {
+ if (!bitmap_set_bit (&contains_head, check->contains[i]))
+ {
+ gfc_error ("%qs directive mentioned multiple times in %s clause in %s "
+ "directive at %L",
+ gfc_ascii_statement (check->contains[i], true),
+ "CONTAINS", gfc_ascii_statement (st), loc);
+ m = MATCH_ERROR;
+ }
+ if (bitmap_bit_p (&absent_head, check->contains[i]))
+ {
+ gfc_error ("%qs directive mentioned both times in ABSENT and CONTAINS "
+ "clauses in %s directive at %L",
+ gfc_ascii_statement (check->absent[i], true),
+ gfc_ascii_statement (st), loc);
+ m = MATCH_ERROR;
+ }
+ }
+
+ if (m == MATCH_ERROR)
+ return MATCH_ERROR;
+ if (merge == NULL)
+ return MATCH_YES;
+ if (merge->absent == NULL && check->absent)
+ {
+ merge->n_absent = check->n_absent;
+ merge->absent = check->absent;
+ check->absent = NULL;
+ }
+ else if (merge->absent && check->absent)
+ {
+ check->absent = XRESIZEVEC (gfc_statement, check->absent,
+ merge->n_absent + check->n_absent);
+ for (int i = 0; i < merge->n_absent; i++)
+ if (!bitmap_bit_p (&absent_head, merge->absent[i]))
+ check->absent[check->n_absent++] = merge->absent[i];
+ free (merge->absent);
+ merge->absent = check->absent;
+ merge->n_absent = check->n_absent;
+ check->absent = NULL;
+ }
+ if (merge->contains == NULL && check->contains)
+ {
+ merge->n_contains = check->n_contains;
+ merge->contains = check->contains;
+ check->contains = NULL;
+ }
+ else if (merge->contains && check->contains)
+ {
+ check->contains = XRESIZEVEC (gfc_statement, check->contains,
+ merge->n_contains + check->n_contains);
+ for (int i = 0; i < merge->n_contains; i++)
+ if (!bitmap_bit_p (&contains_head, merge->contains[i]))
+ check->contains[check->n_contains++] = merge->contains[i];
+ free (merge->contains);
+ merge->contains = check->contains;
+ merge->n_contains = check->n_contains;
+ check->contains = NULL;
+ }
+ return MATCH_YES;
+}
+
/* Match with duplicate check. Matches 'name'. If expr != NULL, it
then matches '(expr)', otherwise, if open_parens is true,
@@ -1511,6 +1766,14 @@ gfc_match_omp_clauses (gfc_omp_clauses **cp, const omp_mask mask,
case 'a':
end_colon = false;
head = NULL;
+ if ((mask & OMP_CLAUSE_ASSUMPTIONS)
+ && gfc_match ("absent ( ") == MATCH_YES)
+ {
+ if (gfc_omp_absent_contains_clause (&c->assume, true)
+ != MATCH_YES)
+ goto error;
+ continue;
+ }
if ((mask & OMP_CLAUSE_ALIGNED)
&& gfc_match_omp_variable_list ("aligned (",
&c->lists[OMP_LIST_ALIGNED],
@@ -1743,6 +2006,14 @@ gfc_match_omp_clauses (gfc_omp_clauses **cp, const omp_mask mask,
needs_space = true;
continue;
}
+ if ((mask & OMP_CLAUSE_ASSUMPTIONS)
+ && gfc_match ("contains ( ") == MATCH_YES)
+ {
+ if (gfc_omp_absent_contains_clause (&c->assume, false)
+ != MATCH_YES)
+ goto error;
+ continue;
+ }
if ((mask & OMP_CLAUSE_COPY)
&& gfc_match ("copy ( ") == MATCH_YES
&& gfc_match_omp_map_clause (&c->lists[OMP_LIST_MAP],
@@ -2277,6 +2548,20 @@ gfc_match_omp_clauses (gfc_omp_clauses **cp, const omp_mask mask,
goto error;
continue;
}
+ if ((mask & OMP_CLAUSE_ASSUMPTIONS)
+ && gfc_match ("holds ( ") == MATCH_YES)
+ {
+ gfc_expr *e;
+ if (gfc_match ("%e )", &e) != MATCH_YES)
+ goto error;
+ if (c->assume == NULL)
+ c->assume = gfc_get_omp_assumptions ();
+ gfc_expr_list *el = XCNEW (gfc_expr_list);
+ el->expr = e;
+ el->next = c->assume->holds;
+ c->assume->holds = el;
+ continue;
+ }
if ((mask & OMP_CLAUSE_HOST_SELF)
&& gfc_match ("host ( ") == MATCH_YES
&& gfc_match_omp_map_clause (&c->lists[OMP_LIST_MAP],
@@ -2664,6 +2949,41 @@ gfc_match_omp_clauses (gfc_omp_clauses **cp, const omp_mask mask,
OMP_MAP_IF_PRESENT, true,
allow_derived))
continue;
+ if ((mask & OMP_CLAUSE_ASSUMPTIONS)
+ && (m = gfc_match_dupl_check (!c->assume
+ || !c->assume->no_openmp_routines,
+ "no_openmp_routines")) == MATCH_YES)
+ {
+ if (m == MATCH_ERROR)
+ goto error;
+ if (c->assume == NULL)
+ c->assume = gfc_get_omp_assumptions ();
+ c->assume->no_openmp_routines = needs_space = true;
+ continue;
+ }
+ if ((mask & OMP_CLAUSE_ASSUMPTIONS)
+ && (m = gfc_match_dupl_check (!c->assume || !c->assume->no_openmp,
+ "no_openmp")) == MATCH_YES)
+ {
+ if (m == MATCH_ERROR)
+ goto error;
+ if (c->assume == NULL)
+ c->assume = gfc_get_omp_assumptions ();
+ c->assume->no_openmp = needs_space = true;
+ continue;
+ }
+ if ((mask & OMP_CLAUSE_ASSUMPTIONS)
+ && (m = gfc_match_dupl_check (!c->assume
+ || !c->assume->no_parallelism,
+ "no_parallelism")) == MATCH_YES)
+ {
+ if (m == MATCH_ERROR)
+ goto error;
+ if (c->assume == NULL)
+ c->assume = gfc_get_omp_assumptions ();
+ c->assume->no_parallelism = needs_space = true;
+ continue;
+ }
if ((mask & OMP_CLAUSE_NOGROUP)
&& (m = gfc_match_dupl_check (!c->nogroup, "nogroup"))
!= MATCH_NO)
@@ -3942,6 +4262,69 @@ match_omp (gfc_exec_op op, const omp_mask mask)
match
+gfc_match_omp_assume (void)
+{
+ gfc_omp_clauses *c;
+ locus loc = gfc_current_locus;
+ if ((gfc_match_omp_clauses (&c, omp_mask (OMP_CLAUSE_ASSUMPTIONS))
+ != MATCH_YES)
+ || (omp_verify_merge_absent_contains (ST_OMP_ASSUME, c->assume, NULL,
+ &loc) != MATCH_YES))
+ return MATCH_ERROR;
+ new_st.op = EXEC_OMP_ASSUME;
+ new_st.ext.omp_clauses = c;
+ return MATCH_YES;
+}
+
+
+match
+gfc_match_omp_assumes (void)
+{
+ gfc_omp_clauses *c;
+ locus loc = gfc_current_locus;
+ if (!gfc_current_ns->proc_name
+ || (gfc_current_ns->proc_name->attr.flavor != FL_MODULE
+ && !gfc_current_ns->proc_name->attr.subroutine
+ && !gfc_current_ns->proc_name->attr.function))
+ {
+ gfc_error ("!$OMP ASSUMES at %C must be in the specification part of a "
+ "subprogram or module");
+ return MATCH_ERROR;
+ }
+ if ((gfc_match_omp_clauses (&c, omp_mask (OMP_CLAUSE_ASSUMPTIONS))
+ != MATCH_YES)
+ || (omp_verify_merge_absent_contains (ST_OMP_ASSUMES, c->assume,
+ gfc_current_ns->omp_assumes, &loc)
+ != MATCH_YES))
+ return MATCH_ERROR;
+ if (gfc_current_ns->omp_assumes == NULL)
+ {
+ gfc_current_ns->omp_assumes = c->assume;
+ c->assume = NULL;
+ }
+ else if (gfc_current_ns->omp_assumes && c->assume)
+ {
+ gfc_current_ns->omp_assumes->no_openmp |= c->assume->no_openmp;
+ gfc_current_ns->omp_assumes->no_openmp_routines
+ |= c->assume->no_openmp_routines;
+ gfc_current_ns->omp_assumes->no_parallelism |= c->assume->no_parallelism;
+ if (gfc_current_ns->omp_assumes->holds && c->assume->holds)
+ {
+ gfc_expr_list *el = gfc_current_ns->omp_assumes->holds;
+ for ( ; el->next ; el = el->next)
+ ;
+ el->next = c->assume->holds;
+ }
+ else if (c->assume->holds)
+ gfc_current_ns->omp_assumes->holds = c->assume->holds;
+ c->assume->holds = NULL;
+ }
+ gfc_free_omp_clauses (c);
+ return MATCH_YES;
+}
+
+
+match
gfc_match_omp_critical (void)
{
char n[GFC_MAX_SYMBOL_LEN+1];
@@ -6505,6 +6888,20 @@ resolve_omp_udr_clause (gfc_omp_namelist *n, gfc_namespace *ns,
return copy;
}
+
+/* Resolve ASSUME's and ASSUMES' assumption clauses. Note that absent/contains
+ is handled during parse time in omp_verify_merge_absent_contains. */
+
+void
+gfc_resolve_omp_assumptions (gfc_omp_assumptions *assume)
+{
+ for (gfc_expr_list *el = assume->holds; el; el = el->next)
+ if (!gfc_resolve_expr (el->expr) || el->expr->ts.type != BT_LOGICAL)
+ gfc_error ("HOLDS expression at %L must be a logical expression",
+ &el->expr->where);
+}
+
+
/* OpenMP directive resolving routines. */
static void
@@ -7888,6 +8285,9 @@ resolve_omp_clauses (gfc_code *code, gfc_omp_clauses *omp_clauses,
gfc_error ("%<DETACH%> clause at %L must not be used together with "
"%<MERGEABLE%> clause", &omp_clauses->detach->where);
}
+
+ if (omp_clauses->assume)
+ gfc_resolve_omp_assumptions (omp_clauses->assume);
}
@@ -9116,6 +9516,8 @@ omp_code_to_statement (gfc_code *code)
return ST_OMP_DO;
case EXEC_OMP_LOOP:
return ST_OMP_LOOP;
+ case EXEC_OMP_ASSUME:
+ return ST_OMP_ASSUME;
case EXEC_OMP_ATOMIC:
return ST_OMP_ATOMIC;
case EXEC_OMP_BARRIER:
@@ -9635,6 +10037,7 @@ gfc_resolve_omp_directive (gfc_code *code, gfc_namespace *ns)
case EXEC_OMP_TEAMS_LOOP:
resolve_omp_do (code);
break;
+ case EXEC_OMP_ASSUME:
case EXEC_OMP_CANCEL:
case EXEC_OMP_ERROR:
case EXEC_OMP_MASKED: