aboutsummaryrefslogtreecommitdiff
path: root/gcc/c
diff options
context:
space:
mode:
authorJakub Jelinek <jakub@redhat.com>2022-09-27 08:23:08 +0200
committerJakub Jelinek <jakub@redhat.com>2022-09-27 08:23:08 +0200
commit4790fe99f236c7f1b617722403e682ba2f82485f (patch)
tree2b247f6e88dc3f479497f74215bf1be01f127232 /gcc/c
parent5b86d5dbe47c477daf739b82c3793a70f8cbd96c (diff)
downloadgcc-4790fe99f236c7f1b617722403e682ba2f82485f.zip
gcc-4790fe99f236c7f1b617722403e682ba2f82485f.tar.gz
gcc-4790fe99f236c7f1b617722403e682ba2f82485f.tar.bz2
openmp: Add OpenMP assume, assumes and begin/end assumes support
The following patch implements OpenMP 5.1 #pragma omp assume #pragma omp assumes and #pragma omp begin assumes #pragma omp end assumes directive support for C and C++. Currently it doesn't remember anything from the assumption clauses for later, so is mainly to support the directives and diagnose errors in their use. If the recently posted C++23 [[assume (cond)]]; support makes it in, the intent is that this can be easily adjusted at least for the #pragma omp assume directive with holds clause(s) to use the same infrastructure. Now, C++23 portable assumptions are slightly different from OpenMP 5.1 assumptions' holds clause in that C++23 assumption holds just where it appears, while OpenMP 5.1 assumptions hold everywhere in the scope of the directive. For assumes directive which can appear at file or namespace scope it is the whole TU and everything that functions from there call at runtime, for begin assumes/end assumes pair all the functions in between those directives and everything they call and for assume directive the associated (currently structured) block. I have no idea how to represents such holds to be usable for optimizers, except to make #pragma omp assume holds (cond) block; expand essentially to [[assume (cond)]]; block; or [[assume (cond)]]; block; [[assume (cond)]]; for now. Except for holds clause, the other assumptions are OpenMP related, I'd say we should brainstorm where it would be useful to optimize based on such information (I guess e.g. in target regions it easily could) and only when we come up with something like that think about how to propagate the assumptions to the optimizers. 2022-09-27 Jakub Jelinek <jakub@redhat.com> gcc/c-family/ * c-pragma.h (enum pragma_kind): Add PRAGMA_OMP_ASSUME, PRAGMA_OMP_ASSUMES and PRAGMA_OMP_BEGIN. Rename PRAGMA_OMP_END_DECLARE_TARGET to PRAGMA_OMP_END. * c-pragma.cc (omp_pragmas): Add assumes and begin. For end rename PRAGMA_OMP_END_DECLARE_TARGET to PRAGMA_OMP_END. (omp_pragmas_simd): Add assume. * c-common.h (c_omp_directives): Declare. * c-omp.cc (omp_directives): Rename to ... (c_omp_directives): ... this. No longer static. Uncomment assume, assumes, begin assumes and end assumes entries. In end declare target entry rename PRAGMA_OMP_END_DECLARE_TARGET to PRAGMA_OMP_END. (c_omp_categorize_directive): Adjust for omp_directives to c_omp_directives renaming. gcc/c/ * c-lang.h (current_omp_begin_assumes): Declare. * c-parser.cc: Include bitmap.h. (c_parser_omp_end_declare_target): Rename to ... (c_parser_omp_end): ... this. Handle also end assumes. (c_parser_omp_begin, c_parser_omp_assumption_clauses, c_parser_omp_assumes, c_parser_omp_assume): New functions. (c_parser_translation_unit): Also diagnose #pragma omp begin assumes without corresponding #pragma omp end assumes. (c_parser_pragma): Use %s in may only be used at file scope diagnostics to decrease number of translatable messages. Handle PRAGMA_OMP_BEGIN and PRAGMA_OMP_ASSUMES. Handle PRAGMA_OMP_END rather than PRAGMA_OMP_END_DECLARE_TARGET and call c_parser_omp_end for it rather than c_parser_omp_end_declare_target. (c_parser_omp_construct): Handle PRAGMA_OMP_ASSUME. * c-decl.cc (current_omp_begin_assumes): Define. gcc/cp/ * cp-tree.h (struct omp_begin_assumes_data): New type. (struct saved_scope): Add omp_begin_assumes member. * parser.cc: Include bitmap.h. (cp_parser_omp_assumption_clauses, cp_parser_omp_assume, cp_parser_omp_assumes, cp_parser_omp_begin): New functions. (cp_parser_omp_end_declare_target): Rename to ... (cp_parser_omp_end): ... this. Handle also end assumes. (cp_parser_omp_construct): Handle PRAGMA_OMP_ASSUME. (cp_parser_pragma): Handle PRAGMA_OMP_ASSUME, PRAGMA_OMP_ASSUMES and PRAGMA_OMP_BEGIN. Handle PRAGMA_OMP_END rather than PRAGMA_OMP_END_DECLARE_TARGET and call cp_parser_omp_end for it rather than cp_parser_omp_end_declare_target. * pt.cc (apply_late_template_attributes): Also temporarily clear omp_begin_assumes. * semantics.cc (finish_translation_unit): Also diagnose #pragma omp begin assumes without corresponding #pragma omp end assumes. gcc/testsuite/ * c-c++-common/gomp/assume-1.c: New test. * c-c++-common/gomp/assume-2.c: New test. * c-c++-common/gomp/assume-3.c: New test. * c-c++-common/gomp/assumes-1.c: New test. * c-c++-common/gomp/assumes-2.c: New test. * c-c++-common/gomp/assumes-3.c: New test. * c-c++-common/gomp/assumes-4.c: New test. * c-c++-common/gomp/begin-assumes-1.c: New test. * c-c++-common/gomp/begin-assumes-2.c: New test. * c-c++-common/gomp/begin-assumes-3.c: New test. * c-c++-common/gomp/begin-assumes-4.c: New test. * c-c++-common/gomp/declare-target-6.c: New test. * g++.dg/gomp/attrs-1.C (bar): Add n1 and n2 arguments, add tests for assume directive. * g++.dg/gomp/attrs-2.C (bar): Likewise. * g++.dg/gomp/attrs-9.C: Add n1 and n2 variables, add tests for begin assumes directive. * g++.dg/gomp/attrs-15.C: New test. * g++.dg/gomp/attrs-16.C: New test. * g++.dg/gomp/attrs-17.C: New test.
Diffstat (limited to 'gcc/c')
-rw-r--r--gcc/c/c-decl.cc4
-rw-r--r--gcc/c/c-lang.h3
-rw-r--r--gcc/c/c-parser.cc308
3 files changed, 298 insertions, 17 deletions
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index b09c639..740982e 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -156,6 +156,10 @@ static bool undef_nested_function;
/* If non-zero, implicit "omp declare target" attribute is added into the
attribute lists. */
int current_omp_declare_target_attribute;
+
+/* If non-zero, we are inside of
+ #pragma omp begin assumes ... #pragma omp end assumes region. */
+int current_omp_begin_assumes;
/* Each c_binding structure describes one binding of an identifier to
a decl. All the decls in a scope - irrespective of namespace - are
diff --git a/gcc/c/c-lang.h b/gcc/c/c-lang.h
index 7bdab47..861abe8 100644
--- a/gcc/c/c-lang.h
+++ b/gcc/c/c-lang.h
@@ -63,5 +63,8 @@ struct GTY(()) language_function {
/* If non-zero, implicit "omp declare target" attribute is added into the
attribute lists. */
extern GTY(()) int current_omp_declare_target_attribute;
+/* Similarly whether we are in between #pragma omp begin assumes and
+ #pragma omp end assumes (and how many times when nested). */
+extern GTY(()) int current_omp_begin_assumes;
#endif /* ! GCC_C_LANG_H */
diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc
index bce79d3..f2498dc 100644
--- a/gcc/c/c-parser.cc
+++ b/gcc/c/c-parser.cc
@@ -71,6 +71,7 @@ along with GCC; see the file COPYING3. If not see
#include "tree-pretty-print.h"
#include "memmodel.h"
#include "c-family/known-headers.h"
+#include "bitmap.h"
/* We need to walk over decls with incomplete struct/union/enum types
after parsing the whole translation unit.
@@ -1594,10 +1595,13 @@ enum pragma_context { pragma_external, pragma_struct, pragma_param,
static bool c_parser_pragma (c_parser *, enum pragma_context, bool *);
static bool c_parser_omp_cancellation_point (c_parser *, enum pragma_context);
static bool c_parser_omp_target (c_parser *, enum pragma_context, bool *);
-static void c_parser_omp_end_declare_target (c_parser *);
+static void c_parser_omp_begin (c_parser *);
+static void c_parser_omp_end (c_parser *);
static bool c_parser_omp_declare (c_parser *, enum pragma_context);
static void c_parser_omp_requires (c_parser *);
static bool c_parser_omp_error (c_parser *, enum pragma_context);
+static void c_parser_omp_assumption_clauses (c_parser *, bool);
+static void c_parser_omp_assumes (c_parser *);
static bool c_parser_omp_ordered (c_parser *, enum pragma_context, bool *);
static void c_parser_oacc_routine (c_parser *, enum pragma_context);
@@ -1678,6 +1682,13 @@ c_parser_translation_unit (c_parser *parser)
"%<#pragma omp end declare target%>");
current_omp_declare_target_attribute = 0;
}
+ if (current_omp_begin_assumes)
+ {
+ if (!errorcount)
+ error ("%<#pragma omp begin assumes%> without corresponding "
+ "%<#pragma omp end assumes%>");
+ current_omp_begin_assumes = 0;
+ }
}
/* Parse an external declaration (C90 6.7, C99 6.9, C11 6.9).
@@ -12594,8 +12605,12 @@ c_parser_pragma (c_parser *parser, enum pragma_context context, bool *if_p)
case PRAGMA_OMP_TARGET:
return c_parser_omp_target (parser, context, if_p);
- case PRAGMA_OMP_END_DECLARE_TARGET:
- c_parser_omp_end_declare_target (parser);
+ case PRAGMA_OMP_BEGIN:
+ c_parser_omp_begin (parser);
+ return false;
+
+ case PRAGMA_OMP_END:
+ c_parser_omp_end (parser);
return false;
case PRAGMA_OMP_SCAN:
@@ -12619,13 +12634,26 @@ c_parser_pragma (c_parser *parser, enum pragma_context context, bool *if_p)
if (context != pragma_external)
{
error_at (c_parser_peek_token (parser)->location,
- "%<#pragma omp requires%> may only be used at file scope");
+ "%<#pragma %s%> may only be used at file scope",
+ "omp requires");
c_parser_skip_until_found (parser, CPP_PRAGMA_EOL, NULL);
return false;
}
c_parser_omp_requires (parser);
return false;
+ case PRAGMA_OMP_ASSUMES:
+ if (context != pragma_external)
+ {
+ error_at (c_parser_peek_token (parser)->location,
+ "%<#pragma %s%> may only be used at file scope",
+ "omp assumes");
+ c_parser_skip_until_found (parser, CPP_PRAGMA_EOL, NULL);
+ return false;
+ }
+ c_parser_omp_assumes (parser);
+ return false;
+
case PRAGMA_OMP_NOTHING:
c_parser_omp_nothing (parser);
return false;
@@ -22405,14 +22433,44 @@ c_parser_omp_declare_target (c_parser *parser)
"directive with only %<device_type%> clauses ignored");
}
+/* OpenMP 5.1
+ #pragma omp begin assumes clauses[optseq] new-line */
+
+static void
+c_parser_omp_begin (c_parser *parser)
+{
+ const char *p = "";
+ c_parser_consume_pragma (parser);
+ if (c_parser_next_token_is (parser, CPP_NAME))
+ p = IDENTIFIER_POINTER (c_parser_peek_token (parser)->value);
+ if (strcmp (p, "assumes") == 0)
+ {
+ c_parser_consume_token (parser);
+ c_parser_omp_assumption_clauses (parser, false);
+ current_omp_begin_assumes++;
+ }
+ else
+ {
+ c_parser_error (parser, "expected %<assumes%>");
+ c_parser_skip_to_pragma_eol (parser);
+ }
+}
+
+/* OpenMP 4.0
+ #pragma omp end declare target
+
+ OpenMP 5.1
+ #pragma omp end assumes */
+
static void
-c_parser_omp_end_declare_target (c_parser *parser)
+c_parser_omp_end (c_parser *parser)
{
location_t loc = c_parser_peek_token (parser)->location;
+ const char *p = "";
c_parser_consume_pragma (parser);
- if (c_parser_next_token_is (parser, CPP_NAME)
- && strcmp (IDENTIFIER_POINTER (c_parser_peek_token (parser)->value),
- "declare") == 0)
+ if (c_parser_next_token_is (parser, CPP_NAME))
+ p = IDENTIFIER_POINTER (c_parser_peek_token (parser)->value);
+ if (strcmp (p, "declare") == 0)
{
c_parser_consume_token (parser);
if (c_parser_next_token_is (parser, CPP_NAME)
@@ -22425,22 +22483,30 @@ c_parser_omp_end_declare_target (c_parser *parser)
c_parser_skip_to_pragma_eol (parser);
return;
}
+ c_parser_skip_to_pragma_eol (parser);
+ if (!current_omp_declare_target_attribute)
+ error_at (loc, "%<#pragma omp end declare target%> without "
+ "corresponding %<#pragma omp declare target%>");
+ else
+ current_omp_declare_target_attribute--;
}
- else
+ else if (strcmp (p, "assumes") == 0)
{
- c_parser_error (parser, "expected %<declare%>");
+ c_parser_consume_token (parser);
c_parser_skip_to_pragma_eol (parser);
- return;
+ if (!current_omp_begin_assumes)
+ error_at (loc, "%<#pragma omp end assumes%> without "
+ "corresponding %<#pragma omp begin assumes%>");
+ else
+ current_omp_begin_assumes--;
}
- c_parser_skip_to_pragma_eol (parser);
- if (!current_omp_declare_target_attribute)
- error_at (loc, "%<#pragma omp end declare target%> without corresponding "
- "%<#pragma omp declare target%>");
else
- current_omp_declare_target_attribute--;
+ {
+ c_parser_error (parser, "expected %<declare%> or %<assumes%>");
+ c_parser_skip_to_pragma_eol (parser);
+ }
}
-
/* OpenMP 4.0
#pragma omp declare reduction (reduction-id : typename-list : expression) \
initializer-clause[opt] new-line
@@ -23299,6 +23365,211 @@ c_parser_omp_error (c_parser *parser, enum pragma_context context)
return false;
}
+/* Assumption clauses:
+ OpenMP 5.1
+ absent (directive-name-list)
+ contains (directive-name-list)
+ holds (expression)
+ no_openmp
+ no_openmp_routines
+ no_parallelism */
+
+static void
+c_parser_omp_assumption_clauses (c_parser *parser, bool is_assume)
+{
+ bool first = true;
+ bool no_openmp = false;
+ bool no_openmp_routines = false;
+ bool no_parallelism = false;
+ bitmap_head absent_head, contains_head;
+
+ bitmap_obstack_initialize (NULL);
+ bitmap_initialize (&absent_head, &bitmap_default_obstack);
+ bitmap_initialize (&contains_head, &bitmap_default_obstack);
+
+ if (c_parser_next_token_is (parser, CPP_PRAGMA_EOL))
+ error_at (c_parser_peek_token (parser)->location,
+ "expected at least one assumption clause");
+
+ while (c_parser_next_token_is_not (parser, CPP_PRAGMA_EOL))
+ {
+ if (!first
+ && c_parser_next_token_is (parser, CPP_COMMA)
+ && c_parser_peek_2nd_token (parser)->type == CPP_NAME)
+ c_parser_consume_token (parser);
+
+ first = false;
+
+ if (!c_parser_next_token_is (parser, CPP_NAME))
+ break;
+
+ const char *p
+ = IDENTIFIER_POINTER (c_parser_peek_token (parser)->value);
+ location_t cloc = c_parser_peek_token (parser)->location;
+
+ if (!strcmp (p, "no_openmp"))
+ {
+ c_parser_consume_token (parser);
+ if (no_openmp)
+ error_at (cloc, "too many %qs clauses", "no_openmp");
+ no_openmp = true;
+ }
+ else if (!strcmp (p, "no_openmp_routines"))
+ {
+ c_parser_consume_token (parser);
+ if (no_openmp_routines)
+ error_at (cloc, "too many %qs clauses", "no_openmp_routines");
+ no_openmp_routines = true;
+ }
+ else if (!strcmp (p, "no_parallelism"))
+ {
+ c_parser_consume_token (parser);
+ if (no_parallelism)
+ error_at (cloc, "too many %qs clauses", "no_parallelism");
+ no_parallelism = true;
+ }
+ else if (!strcmp (p, "holds"))
+ {
+ c_parser_consume_token (parser);
+ matching_parens parens;
+ if (parens.require_open (parser))
+ {
+ location_t eloc = c_parser_peek_token (parser)->location;
+ c_expr expr = c_parser_expr_no_commas (parser, NULL);
+ tree t = convert_lvalue_to_rvalue (eloc, expr, true, true).value;
+ t = c_objc_common_truthvalue_conversion (eloc, t);
+ t = c_fully_fold (t, false, NULL);
+ if (is_assume)
+ {
+ /* FIXME: Emit .ASSUME (t) call here. */
+ (void) t;
+ }
+ parens.skip_until_found_close (parser);
+ }
+ }
+ else if (!strcmp (p, "absent") || !strcmp (p, "contains"))
+ {
+ c_parser_consume_token (parser);
+ matching_parens parens;
+ if (parens.require_open (parser))
+ {
+ do
+ {
+ const char *directive[3] = {};
+ int i;
+ location_t dloc = c_parser_peek_token (parser)->location;
+ for (i = 0; i < 3; i++)
+ {
+ tree id;
+ if (c_parser_peek_nth_token (parser, i + 1)->type
+ == CPP_NAME)
+ id = c_parser_peek_nth_token (parser, i + 1)->value;
+ else if (c_parser_peek_nth_token (parser, i + 1)->keyword
+ != RID_MAX)
+ {
+ enum rid rid
+ = c_parser_peek_nth_token (parser, i + 1)->keyword;
+ id = ridpointers[rid];
+ }
+ else
+ break;
+ directive[i] = IDENTIFIER_POINTER (id);
+ }
+ if (i == 0)
+ error_at (dloc, "expected directive name");
+ else
+ {
+ const struct c_omp_directive *dir
+ = c_omp_categorize_directive (directive[0],
+ directive[1],
+ directive[2]);
+ if (dir == NULL
+ || dir->kind == C_OMP_DIR_DECLARATIVE
+ || dir->kind == C_OMP_DIR_INFORMATIONAL
+ || dir->id == PRAGMA_OMP_END
+ || (!dir->second && directive[1])
+ || (!dir->third && directive[2]))
+ error_at (dloc, "unknown OpenMP directive name in "
+ "%qs clause argument", p);
+ else
+ {
+ int id = dir - c_omp_directives;
+ if (bitmap_bit_p (p[0] == 'a' ? &contains_head
+ : &absent_head, id))
+ error_at (dloc, "%<%s%s%s%s%s%> directive "
+ "mentioned in both %<absent%> and "
+ "%<contains%> clauses",
+ directive[0],
+ directive[1] ? " " : "",
+ directive[1] ? directive[1] : "",
+ directive[2] ? " " : "",
+ directive[2] ? directive[2] : "");
+ else if (!bitmap_set_bit (p[0] == 'a'
+ ? &absent_head
+ : &contains_head, id))
+ error_at (dloc, "%<%s%s%s%s%s%> directive "
+ "mentioned multiple times in %qs "
+ "clauses",
+ directive[0],
+ directive[1] ? " " : "",
+ directive[1] ? directive[1] : "",
+ directive[2] ? " " : "",
+ directive[2] ? directive[2] : "", p);
+ }
+ for (; i; --i)
+ c_parser_consume_token (parser);
+ }
+ if (c_parser_next_token_is (parser, CPP_COMMA))
+ c_parser_consume_token (parser);
+ else
+ break;
+ }
+ while (1);
+ parens.skip_until_found_close (parser);
+ }
+ }
+ else if (startswith (p, "ext_"))
+ {
+ warning_at (cloc, 0, "unknown assumption clause %qs", p);
+ c_parser_consume_token (parser);
+ if (c_parser_next_token_is (parser, CPP_OPEN_PAREN))
+ {
+ matching_parens parens;
+ parens.consume_open (parser);
+ c_parser_balanced_token_sequence (parser);
+ parens.require_close (parser);
+ }
+ }
+ else
+ {
+ c_parser_consume_token (parser);
+ error_at (cloc, "expected assumption clause");
+ break;
+ }
+ }
+ c_parser_skip_to_pragma_eol (parser);
+}
+
+/* OpenMP 5.1
+ #pragma omp assume clauses[optseq] new-line */
+
+static void
+c_parser_omp_assume (c_parser *parser, bool *if_p)
+{
+ c_parser_omp_assumption_clauses (parser, true);
+ add_stmt (c_parser_omp_structured_block (parser, if_p));
+}
+
+/* OpenMP 5.1
+ #pragma omp assumes clauses[optseq] new-line */
+
+static void
+c_parser_omp_assumes (c_parser *parser)
+{
+ c_parser_consume_pragma (parser);
+ c_parser_omp_assumption_clauses (parser, false);
+}
+
/* Main entry point to parsing most OpenMP pragmas. */
static void
@@ -23404,6 +23675,9 @@ c_parser_omp_construct (c_parser *parser, bool *if_p)
strcpy (p_name, "#pragma omp");
stmt = c_parser_omp_teams (loc, parser, p_name, mask, NULL, if_p);
break;
+ case PRAGMA_OMP_ASSUME:
+ c_parser_omp_assume (parser, if_p);
+ return;
default:
gcc_unreachable ();
}