diff mbox series

[committed] openmp: Implement the error directive

Message ID 20210820094529.GD2380545@tucnak
State New
Headers show
Series [committed] openmp: Implement the error directive | expand

Commit Message

Jakub Jelinek Aug. 20, 2021, 9:45 a.m. UTC
Hi!

This patch implements the error directive.  Depending on clauses it is either
a compile time diagnostics (in that case diagnosed right away) or runtime
diagnostics (libgomp API call that diagnoses at runtime), and either fatal
or warning (error or warning at compile time or fatal error vs. error at
runtime) and either has no message or user supplied message (this kind of
e.g. deprecated attribute).  The directive is also stand-alone directive
when at runtime while utility (thus disappears from the IL as if it wasn't
there for parsing like nothing directive) at compile time.

There are some clarifications in the works ATM, so this patch doesn't yet
require that for compile time diagnostics the user message must be a constant
string literal, there are uncertainities on what exactly is valid argument
of message clause (whether just const char * type, convertible to const char *,
qualified/unqualified const char * or char * or what else) and what to do
in templates.  Currently even in templates it is diagnosed right away for
compile time diagnostics, if we'll need to substitute it, we'd need to queue
something into the IL, have pt.c handle it and diagnose only later.

Bootstrapped/regtested on x86_64-linux, committed to trunk.

2021-08-20  Jakub Jelinek  <jakub@redhat.com>

gcc/
	* omp-builtins.def (BUILT_IN_GOMP_WARNING, BUILT_IN_GOMP_ERROR): New
	builtins.
gcc/c-family/
	* c-pragma.h (enum pragma_kind): Add PRAGMA_OMP_ERROR.
	* c-pragma.c (omp_pragmas): Add error directive.
	* c-omp.c (omp_directives): Uncomment error directive entry.
gcc/c/
	* c-parser.c (c_parser_omp_error): New function.
	(c_parser_pragma): Handle PRAGMA_OMP_ERROR.
gcc/cp/
	* parser.c (cp_parser_handle_statement_omp_attributes): Determine if
	PRAGMA_OMP_ERROR directive is C_OMP_DIR_STANDALONE.
	(cp_parser_omp_error): New function.
	(cp_parser_pragma): Handle PRAGMA_OMP_ERROR.
gcc/fortran/
	* types.def (BT_FN_VOID_CONST_PTR_SIZE): New DEF_FUNCTION_TYPE_2.
	* f95-lang.c (ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST): Define.
gcc/testsuite/
	* c-c++-common/gomp/error-1.c: New test.
	* c-c++-common/gomp/error-2.c: New test.
	* c-c++-common/gomp/error-3.c: New test.
	* g++.dg/gomp/attrs-1.C (bar): Add error directive test.
	* g++.dg/gomp/attrs-2.C (bar): Add error directive test.
	* g++.dg/gomp/attrs-13.C: New test.
	* g++.dg/gomp/error-1.C: New test.
libgomp/
	* libgomp.map (GOMP_5.1): Add GOMP_error and GOMP_warning.
	* libgomp_g.h (GOMP_warning, GOMP_error): Declare.
	* error.c (GOMP_warning, GOMP_error): New functions.
	* testsuite/libgomp.c-c++-common/error-1.c: New test.


	Jakub

Comments

Thomas Schwinge Aug. 20, 2021, 1:11 p.m. UTC | #1
Hi!

On 2021-08-20T11:45:29+0200, Jakub Jelinek via Gcc-patches <gcc-patches@gcc.gnu.org> wrote:
> --- libgomp/error.c.jj        2021-08-19 12:53:44.693106618 +0200
> +++ libgomp/error.c   2021-08-19 17:58:55.633203432 +0200

> +void
> +GOMP_warning (const char *msg, size_t msglen)
> +{
> +  if (msg && msglen == (size_t) -1)
> +    gomp_error ("error directive encountered: %s", msg);
> +  else if (msg)
> +    {
> +      fputs ("\nlibgomp: error directive encountered: ", stderr);
> +      fwrite (msg, 1, msglen, stderr);
> +      fputc ('\n', stderr);
> +    }
> +  else
> +    gomp_error ("error directive encountered");
> +}
> +
> +void
> +GOMP_error (const char *msg, size_t msglen)
> +{
> +  if (msg && msglen == (size_t) -1)
> +    gomp_fatal ("fatal error: error directive encountered: %s", msg);
> +  else if (msg)
> +    {
> +      fputs ("\nlibgomp: fatal error: error directive encountered: ", stderr);
> +      fwrite (msg, 1, msglen, stderr);
> +      fputc ('\n', stderr);
> +      exit (EXIT_FAILURE);
> +    }
> +  else
> +    gomp_fatal ("fatal error: error directive encountered");
> +}

At least for nvptx offloading, and at least given the newlib sources I'm
using, the 'fputs'/'fwrite' calls here drag in 'isatty', which isn't
provided by my nvptx newlib at present, so we get, for example:

    [...]
    FAIL: libgomp.c/../libgomp.c-c++-common/declare_target-1.c (test for excess errors)
    Excess errors:
    unresolved symbol isatty
    mkoffload: fatal error: [...]/build-gcc/./gcc/x86_64-pc-linux-gnu-accel-nvptx-none-gcc returned 1 exit status
    [...]

..., and many more.

Now, there are many ways of addressing this...  The most simple one:
conditionalize these 'GOMP_warning'/'GOMP_error' definitions on
'#ifndef LIBGOMP_OFFLOADED_ONLY' is not possible here, because it's
permissible to use the 'error' directive also inside 'target' regions, as
far as I can tell?


Grüße
 Thomas
-----------------
Siemens Electronic Design Automation GmbH; Anschrift: Arnulfstraße 201, 80634 München; Gesellschaft mit beschränkter Haftung; Geschäftsführer: Thomas Heurung, Frank Thürauf; Sitz der Gesellschaft: München; Registergericht München, HRB 106955
Thomas Schwinge Aug. 20, 2021, 1:16 p.m. UTC | #2
Hi!

On 2021-08-20T15:11:45+0200, I wrote:
> On 2021-08-20T11:45:29+0200, Jakub Jelinek via Gcc-patches <gcc-patches@gcc.gnu.org> wrote:
>> --- libgomp/error.c.jj       2021-08-19 12:53:44.693106618 +0200
>> +++ libgomp/error.c  2021-08-19 17:58:55.633203432 +0200
>
>> +void
>> +GOMP_warning (const char *msg, size_t msglen)
>> +{
>> +  if (msg && msglen == (size_t) -1)
>> +    gomp_error ("error directive encountered: %s", msg);
>> +  else if (msg)
>> +    {
>> +      fputs ("\nlibgomp: error directive encountered: ", stderr);
>> +      fwrite (msg, 1, msglen, stderr);
>> +      fputc ('\n', stderr);
>> +    }
>> +  else
>> +    gomp_error ("error directive encountered");
>> +}
>> +
>> +void
>> +GOMP_error (const char *msg, size_t msglen)
>> +{
>> +  if (msg && msglen == (size_t) -1)
>> +    gomp_fatal ("fatal error: error directive encountered: %s", msg);
>> +  else if (msg)
>> +    {
>> +      fputs ("\nlibgomp: fatal error: error directive encountered: ", stderr);
>> +      fwrite (msg, 1, msglen, stderr);
>> +      fputc ('\n', stderr);
>> +      exit (EXIT_FAILURE);
>> +    }
>> +  else
>> +    gomp_fatal ("fatal error: error directive encountered");
>> +}
>
> At least for nvptx offloading, and at least given the newlib sources I'm
> using, the 'fputs'/'fwrite' calls here drag in 'isatty', which isn't
> provided by my nvptx newlib at present, so we get, for example:
>
>     [...]
>     FAIL: libgomp.c/../libgomp.c-c++-common/declare_target-1.c (test for excess errors)
>     Excess errors:
>     unresolved symbol isatty
>     mkoffload: fatal error: [...]/build-gcc/./gcc/x86_64-pc-linux-gnu-accel-nvptx-none-gcc returned 1 exit status
>     [...]
>
> ..., and many more.
>
> Now, there are many ways of addressing this...  The most simple one:
> conditionalize these 'GOMP_warning'/'GOMP_error' definitions on
> '#ifndef LIBGOMP_OFFLOADED_ONLY' is not possible here, because it's
> permissible to use the 'error' directive also inside 'target' regions, as
> far as I can tell?

Ah, I just re-discovered 'libgomp/config/nvptx/error.c' -- I'll cook
something up.


Grüße
 Thomas
-----------------
Siemens Electronic Design Automation GmbH; Anschrift: Arnulfstraße 201, 80634 München; Gesellschaft mit beschränkter Haftung; Geschäftsführer: Thomas Heurung, Frank Thürauf; Sitz der Gesellschaft: München; Registergericht München, HRB 106955
Jakub Jelinek Aug. 20, 2021, 1:21 p.m. UTC | #3
On Fri, Aug 20, 2021 at 03:11:45PM +0200, Thomas Schwinge wrote:
> > --- libgomp/error.c.jj        2021-08-19 12:53:44.693106618 +0200
> > +++ libgomp/error.c   2021-08-19 17:58:55.633203432 +0200
> 
> > +void
> > +GOMP_warning (const char *msg, size_t msglen)
> > +{
> > +  if (msg && msglen == (size_t) -1)
> > +    gomp_error ("error directive encountered: %s", msg);
> > +  else if (msg)
> > +    {
> > +      fputs ("\nlibgomp: error directive encountered: ", stderr);
> > +      fwrite (msg, 1, msglen, stderr);
> > +      fputc ('\n', stderr);
> > +    }
> > +  else
> > +    gomp_error ("error directive encountered");
> > +}
> > +
> > +void
> > +GOMP_error (const char *msg, size_t msglen)
> > +{
> > +  if (msg && msglen == (size_t) -1)
> > +    gomp_fatal ("fatal error: error directive encountered: %s", msg);
> > +  else if (msg)
> > +    {
> > +      fputs ("\nlibgomp: fatal error: error directive encountered: ", stderr);
> > +      fwrite (msg, 1, msglen, stderr);
> > +      fputc ('\n', stderr);
> > +      exit (EXIT_FAILURE);
> > +    }
> > +  else
> > +    gomp_fatal ("fatal error: error directive encountered");
> > +}
> 
> At least for nvptx offloading, and at least given the newlib sources I'm
> using, the 'fputs'/'fwrite' calls here drag in 'isatty', which isn't
> provided by my nvptx newlib at present, so we get, for example:

fputs/fputc/vfprintf/exit/stderr have been in use by error.c already before,
so this must be the fwrite call.
The above is for Fortran which doesn't have zero terminated strings.
Initially I wanted to use instead ... encountered: %.*s", (int) msglen, stderr);
which doesn't handle > 2GB messages, but with offloading who cares, nobody
sane would be trying to print > 2GB messages from offloading regions.

The question is if it should be achieved through copy of error.c in
config/nvptx/, or just include_next there with say fwrite redefined as a
macro that does fprintf ("%.*s", (int) msglen, msg, file)?

> Now, there are many ways of addressing this...  The most simple one:
> conditionalize these 'GOMP_warning'/'GOMP_error' definitions on
> '#ifndef LIBGOMP_OFFLOADED_ONLY' is not possible here, because it's
> permissible to use the 'error' directive also inside 'target' regions, as
> far as I can tell?

!$omp error at(execution) message('whatever')
can be used in offloading regions.

	Jakub
Thomas Schwinge Aug. 20, 2021, 1:54 p.m. UTC | #4
Hi Jakub!

On 2021-08-20T15:21:12+0200, Jakub Jelinek <jakub@redhat.com> wrote:
> On Fri, Aug 20, 2021 at 03:11:45PM +0200, Thomas Schwinge wrote:
>> > --- libgomp/error.c.jj        2021-08-19 12:53:44.693106618 +0200
>> > +++ libgomp/error.c   2021-08-19 17:58:55.633203432 +0200
>>
>> > +void
>> > +GOMP_warning (const char *msg, size_t msglen)
>> > +{
>> > +  if (msg && msglen == (size_t) -1)
>> > +    gomp_error ("error directive encountered: %s", msg);
>> > +  else if (msg)
>> > +    {
>> > +      fputs ("\nlibgomp: error directive encountered: ", stderr);
>> > +      fwrite (msg, 1, msglen, stderr);
>> > +      fputc ('\n', stderr);
>> > +    }
>> > +  else
>> > +    gomp_error ("error directive encountered");
>> > +}
>> > +
>> > +void
>> > +GOMP_error (const char *msg, size_t msglen)
>> > +{
>> > +  if (msg && msglen == (size_t) -1)
>> > +    gomp_fatal ("fatal error: error directive encountered: %s", msg);
>> > +  else if (msg)
>> > +    {
>> > +      fputs ("\nlibgomp: fatal error: error directive encountered: ", stderr);
>> > +      fwrite (msg, 1, msglen, stderr);
>> > +      fputc ('\n', stderr);
>> > +      exit (EXIT_FAILURE);
>> > +    }
>> > +  else
>> > +    gomp_fatal ("fatal error: error directive encountered");
>> > +}
>>
>> At least for nvptx offloading, and at least given the newlib sources I'm
>> using, the 'fputs'/'fwrite' calls here drag in 'isatty', which isn't
>> provided by my nvptx newlib at present, so we get, for example:
>
> fputs/fputc/vfprintf/exit/stderr have been in use by error.c already before,
> so this must be the fwrite call.

ACK.

> The above is for Fortran which doesn't have zero terminated strings.
> Initially I wanted to use instead ... encountered: %.*s", (int) msglen, stderr);
> which doesn't handle > 2GB messages, but with offloading who cares, nobody
> sane would be trying to print > 2GB messages from offloading regions.

(... likewise from the host...)  ;-)

> The question is if it should be achieved through copy of error.c in
> config/nvptx/, or just include_next there with say fwrite redefined as a
> macro that does fprintf ("%.*s", (int) msglen, msg, file)?

(Right, that was also my plan.)

| Ah, I just re-discovered 'libgomp/config/nvptx/error.c' -- I'll cook
| something up.

So, guess what this newlib 'printf ("%.*s", [...]);' prints?
Yes: literal '%.*s'...  Next try: a 'fputc' loop?

See attached "[WIP] Make the OpenMP 'error' directive work for nvptx
offloading".

>> Now, there are many ways of addressing this...  The most simple one:
>> conditionalize these 'GOMP_warning'/'GOMP_error' definitions on
>> '#ifndef LIBGOMP_OFFLOADED_ONLY' is not possible here, because it's
>> permissible to use the 'error' directive also inside 'target' regions, as
>> far as I can tell?
>
> !$omp error at(execution) message('whatever')
> can be used in offloading regions.

Yes, generally works, but at least for Fortran, 'severity (fatal)' seems
to cause a hang, so another thing to be looked into...


Grüße
 Thomas


-----------------
Siemens Electronic Design Automation GmbH; Anschrift: Arnulfstraße 201, 80634 München; Gesellschaft mit beschränkter Haftung; Geschäftsführer: Thomas Heurung, Frank Thürauf; Sitz der Gesellschaft: München; Registergericht München, HRB 106955
Thomas Schwinge Aug. 20, 2021, 10:21 p.m. UTC | #5
Hi!

On 2021-08-20T15:54:34+0200, I wrote:
> On 2021-08-20T15:21:12+0200, Jakub Jelinek <jakub@redhat.com> wrote:
>> On Fri, Aug 20, 2021 at 03:11:45PM +0200, Thomas Schwinge wrote:
>>> > --- libgomp/error.c.jj        2021-08-19 12:53:44.693106618 +0200
>>> > +++ libgomp/error.c   2021-08-19 17:58:55.633203432 +0200
>>>
>>> > +void
>>> > +GOMP_warning (const char *msg, size_t msglen)
>>> > +{
>>> > +  if (msg && msglen == (size_t) -1)
>>> > +    gomp_error ("error directive encountered: %s", msg);
>>> > +  else if (msg)
>>> > +    {
>>> > +      fputs ("\nlibgomp: error directive encountered: ", stderr);
>>> > +      fwrite (msg, 1, msglen, stderr);
>>> > +      fputc ('\n', stderr);
>>> > +    }
>>> > +  else
>>> > +    gomp_error ("error directive encountered");
>>> > +}
>>> > +
>>> > +void
>>> > +GOMP_error (const char *msg, size_t msglen)
>>> > +{
>>> > +  if (msg && msglen == (size_t) -1)
>>> > +    gomp_fatal ("fatal error: error directive encountered: %s", msg);
>>> > +  else if (msg)
>>> > +    {
>>> > +      fputs ("\nlibgomp: fatal error: error directive encountered: ", stderr);
>>> > +      fwrite (msg, 1, msglen, stderr);
>>> > +      fputc ('\n', stderr);
>>> > +      exit (EXIT_FAILURE);
>>> > +    }
>>> > +  else
>>> > +    gomp_fatal ("fatal error: error directive encountered");
>>> > +}
>>>
>>> At least for nvptx offloading, and at least given the newlib sources I'm
>>> using, the 'fputs'/'fwrite' calls here drag in 'isatty', which isn't
>>> provided by my nvptx newlib at present, so we get, for example:
>>
>> fputs/fputc/vfprintf/exit/stderr have been in use by error.c already before,
>> so this must be the fwrite call.
>
> ACK.
>
>> The above is for Fortran which doesn't have zero terminated strings.
>> Initially I wanted to use instead ... encountered: %.*s", (int) msglen, stderr);
>> which doesn't handle > 2GB messages, but with offloading who cares, nobody
>> sane would be trying to print > 2GB messages from offloading regions.
>
> (... likewise from the host...)  ;-)
>
>> The question is if it should be achieved through copy of error.c in
>> config/nvptx/, or just include_next there with say fwrite redefined as a
>> macro that does fprintf ("%.*s", (int) msglen, msg, file)?
>
> (Right, that was also my plan.)
>
> | Ah, I just re-discovered 'libgomp/config/nvptx/error.c' -- I'll cook
> | something up.
>
> So, guess what this newlib 'printf ("%.*s", [...]);' prints?
> Yes: literal '%.*s'...  Next try: a 'fputc' loop?

Did that; "works".  But actually, I think that's good enough for the
intended purpose: there's not much point in optimizing the OpenMP 'error'
directive as long as we still have more than enough real
correctness/performance tasks to be worked on.

Tobias suggested using 'fputc_unlocked', "avoiding repreated locks and
locking for a single stderr char is also pointless", but it's not clear
to me if that's safe to do given that a ton of threads may be hammering
on this in parallel; it's not clear to me if there isn't any
newlib-internal state that needs to be accessed in a serialized way (even
if no actual 'FILE *stream' is involved here)?


>>> permissible to use the 'error' directive also inside 'target' regions, as
>>> far as I can tell?
>>
>> !$omp error at(execution) message('whatever')
>> can be used in offloading regions.

(Also should add test cases for OpenMP 'error' with 'at (execution)' from
deep inside parallelized loop nests, etc., offloaded and non-offloaded?)


> Yes, generally works, but at least for Fortran, 'severity (fatal)' seems
> to cause a hang, so another thing to be looked into...

We thus additionally acquired in 'libgomp/config/nvptx/error.c':

    +/* The 'exit (EXIT_FAILURE);' of an Fortran (only, huh?) OpenMP 'error'
    +   directive with 'severity (fatal)' causes a hang, so 'abort' instead of
    +   'exit'.  */
    +#undef exit
    +#define exit(status) abort ()

... which is another thing to be resolved incrementally.  (Plus adding
corresponding test cases for OpenMP 'error' with 'at (execution)' and
'severity (fatal)' inside OpenMP 'target'.)


Is the attached "Make the OpenMP 'error' directive work for nvptx
offloading" OK to push for now?


Grüße
 Thomas


-----------------
Siemens Electronic Design Automation GmbH; Anschrift: Arnulfstraße 201, 80634 München; Gesellschaft mit beschränkter Haftung; Geschäftsführer: Thomas Heurung, Frank Thürauf; Sitz der Gesellschaft: München; Registergericht München, HRB 106955
Jakub Jelinek Aug. 21, 2021, 5:44 a.m. UTC | #6
On Sat, Aug 21, 2021 at 12:21:41AM +0200, Thomas Schwinge wrote:
> Fix up for recent commit 0d973c0a0d90a0a302e7eda1a4d9709be3c5b102
> "openmp: Implement the error directive".
> ---
>  libgomp/config/nvptx/error.c                  | 32 +++++++++++++++++--
>  .../testsuite/libgomp.c-c++-common/error-1.c  | 10 ++++++
>  libgomp/testsuite/libgomp.fortran/error-1.f90 |  9 ++++++
>  3 files changed, 48 insertions(+), 3 deletions(-)

As we only use it with size equal to literal 1, I guess it is ok that way,
otherwise it would be nice to at least precompute size * nmemb just once
instead of every iteration.

Ok.

> diff --git a/libgomp/config/nvptx/error.c b/libgomp/config/nvptx/error.c
> index dfa75da354f..c55791e34b4 100644
> --- a/libgomp/config/nvptx/error.c
> +++ b/libgomp/config/nvptx/error.c
> @@ -31,12 +31,38 @@
>  #include <stdio.h>
>  #include <stdlib.h>
>  
> -#undef vfprintf
> -#undef fputs
> -#undef fputc
>  
> +/* No 'FILE *stream's, just basic 'vprintf' etc.  */
> +
> +#undef vfprintf
>  #define vfprintf(stream, fmt, list) vprintf (fmt, list)
> +
> +#undef fputs
>  #define fputs(s, stream) printf ("%s", s)
> +
> +#undef fputc
>  #define fputc(c, stream) printf ("%c", c)
>  
> +#undef fwrite
> +#if 0
> +# define fwrite(ptr, size, nmemb, stream) \
> +  printf ("%.*s", (int) (size * nmemb), (int) (size * nmemb), ptr)
> +/* ... prints literal '%.*s'.  */
> +#else
> +# define fwrite(ptr, size, nmemb, stream) \
> +  do { \
> +    /* Yuck!  */ \
> +    for (size_t i = 0; i < size * nmemb; ++i) \
> +      printf ("%c", ptr[i]); \
> +  } while (0)
> +#endif
> +
> +
> +/* The 'exit (EXIT_FAILURE);' of an Fortran (only, huh?) OpenMP 'error'
> +   directive with 'severity (fatal)' causes a hang, so 'abort' instead of
> +   'exit'.  */
> +#undef exit
> +#define exit(status) abort ()
> +
> +
>  #include "../../error.c"
> diff --git a/libgomp/testsuite/libgomp.c-c++-common/error-1.c b/libgomp/testsuite/libgomp.c-c++-common/error-1.c
> index 5f454c1adaa..04c0519bf63 100644
> --- a/libgomp/testsuite/libgomp.c-c++-common/error-1.c
> +++ b/libgomp/testsuite/libgomp.c-c++-common/error-1.c
> @@ -34,11 +34,20 @@ foo (int i, int x)
>  int
>  main ()
>  {
> +  /* Initialize offloading early, so that any output this may produce doesn't
> +     disturb the 'dg-output' scanning below.  */
> +  #pragma omp target
> +  ;
> +
>    if (foo (5, 0) != 13 || foo (6, 1) != 17)
>      abort ();
>    #pragma omp error at (execution) severity (warning)
>    const char *msg = "my message" + 2;
>    #pragma omp error at (execution) severity (warning) message (msg + 1)
> +  #pragma omp target
> +  {
> +    #pragma omp error at (execution) severity (warning) message ("hello from a distance")
> +  }
>    #pragma omp error at (execution) severity (fatal) message (msg - 2)
>    #pragma omp error at (execution) severity (warning) message ("foobar")
>    return 0;
> @@ -46,4 +55,5 @@ main ()
>  
>  /* { dg-output "libgomp: error directive encountered(\n|\r|\n\r)(\n|\r|\n\r)" } */
>  /* { dg-output "libgomp: error directive encountered: message(\n|\r|\n\r)(\n|\r|\n\r)" } */
> +/* { dg-output "libgomp: error directive encountered: hello from a distance(\n|\r|\n\r)(\n|\r|\n\r)" } */
>  /* { dg-output "libgomp: fatal error: error directive encountered: my message" } */
> diff --git a/libgomp/testsuite/libgomp.fortran/error-1.f90 b/libgomp/testsuite/libgomp.fortran/error-1.f90
> index 92c246cfcaf..7c497fd002e 100644
> --- a/libgomp/testsuite/libgomp.fortran/error-1.f90
> +++ b/libgomp/testsuite/libgomp.fortran/error-1.f90
> @@ -37,6 +37,11 @@ program main
>    character(len=13) :: msg
>    character(len=:), allocatable :: msg2, msg3
>  
> +  ! Initialize offloading early, so that any output this may produce doesn't
> +  ! disturb the 'dg-output' scanning below.
> +  !$omp target
> +  !$omp end target
> +
>    msg = "my message"
>    if (foo (5, 0) /= 15 .or. foo (7, 1) /= 16) &
>      stop 1
> @@ -47,6 +52,9 @@ program main
>    !$omp error at (execution) severity (warning)
>    !$omp error at (execution) severity (warning) message(trim(msg(4:)))
>    !$omp error at (execution) severity (warning) message ("Farewell")
> +  !$omp target
> +  !$omp error at (execution) severity (warning) message ("ffrom a distanceee"(2:16))
> +  !$omp end target
>    !$omp error at (execution) severity (warning) message (msg2)
>    !$omp error at (execution) severity (warning) message (msg(4:6))
>    !$omp error at (execution) severity (fatal) message (msg)
> @@ -73,6 +81,7 @@ end
>  ! { dg-output "libgomp: error directive encountered(\n|\r|\n\r)(\n|\r|\n\r)" }
>  ! { dg-output "libgomp: error directive encountered: message(\n|\r|\n\r)(\n|\r|\n\r)" }
>  ! { dg-output "libgomp: error directive encountered: Farewell(\n|\r|\n\r)(\n|\r|\n\r)" }
> +! { dg-output "libgomp: error directive encountered: from a distance(\n|\r|\n\r)(\n|\r|\n\r)" }
>  ! { dg-output "libgomp: error directive encountered: Hello World(\n|\r|\n\r)(\n|\r|\n\r)" }
>  ! { dg-output "libgomp: error directive encountered: mes(\n|\r|\n\r)(\n|\r|\n\r)" }
>  ! { dg-output "libgomp: fatal error: error directive encountered: my message   (\n|\r|\n\r)" }

	Jakub
diff mbox series

Patch

--- gcc/omp-builtins.def.jj	2021-08-19 12:53:44.693106618 +0200
+++ gcc/omp-builtins.def	2021-08-19 17:46:45.960368837 +0200
@@ -463,3 +463,7 @@  DEF_GOMP_BUILTIN (BUILT_IN_GOMP_ALLOC,
 		  ATTR_ALLOC_WARN_UNUSED_RESULT_SIZE_2_NOTHROW_LIST)
 DEF_GOMP_BUILTIN (BUILT_IN_GOMP_FREE,
 		  "GOMP_free", BT_FN_VOID_PTR_PTRMODE, ATTR_NOTHROW_LEAF_LIST)
+DEF_GOMP_BUILTIN (BUILT_IN_GOMP_WARNING, "GOMP_warning",
+		  BT_FN_VOID_CONST_PTR_SIZE, ATTR_NOTHROW_LEAF_LIST)
+DEF_GOMP_BUILTIN (BUILT_IN_GOMP_ERROR, "GOMP_error",
+		  BT_FN_VOID_CONST_PTR_SIZE, ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST)
--- gcc/c-family/c-pragma.h.jj	2021-08-19 12:53:44.690106660 +0200
+++ gcc/c-family/c-pragma.h	2021-08-19 14:37:58.004167196 +0200
@@ -53,6 +53,7 @@  enum pragma_kind {
   PRAGMA_OMP_DECLARE,
   PRAGMA_OMP_DEPOBJ,
   PRAGMA_OMP_DISTRIBUTE,
+  PRAGMA_OMP_ERROR,
   PRAGMA_OMP_END_DECLARE_TARGET,
   PRAGMA_OMP_FLUSH,
   PRAGMA_OMP_FOR,
--- gcc/c-family/c-pragma.c.jj	2021-08-19 12:53:44.690106660 +0200
+++ gcc/c-family/c-pragma.c	2021-08-19 14:37:58.004167196 +0200
@@ -1326,6 +1326,7 @@  static const struct omp_pragma_def omp_p
   { "cancellation", PRAGMA_OMP_CANCELLATION_POINT },
   { "critical", PRAGMA_OMP_CRITICAL },
   { "depobj", PRAGMA_OMP_DEPOBJ },
+  { "error", PRAGMA_OMP_ERROR },
   { "end", PRAGMA_OMP_END_DECLARE_TARGET },
   { "flush", PRAGMA_OMP_FLUSH },
   { "nothing", PRAGMA_OMP_NOTHING },
--- gcc/c-family/c-omp.c.jj	2021-08-19 12:53:44.690106660 +0200
+++ gcc/c-family/c-omp.c	2021-08-19 14:37:58.004167196 +0200
@@ -2991,8 +2991,8 @@  static const struct c_omp_directive omp_
   /* { "end", "metadirective", nullptr, PRAGMA_OMP_END,
     C_OMP_DIR_???, ??? },  */
   /* error with at(execution) is C_OMP_DIR_STANDALONE.  */
-  /* { "error", nullptr, nullptr, PRAGMA_OMP_ERROR,
-    C_OMP_DIR_UTILITY, false },  */
+  { "error", nullptr, nullptr, PRAGMA_OMP_ERROR,
+    C_OMP_DIR_UTILITY, false },
   { "flush", nullptr, nullptr, PRAGMA_OMP_FLUSH,
     C_OMP_DIR_STANDALONE, false },
   { "for", nullptr, nullptr, PRAGMA_OMP_FOR,
--- gcc/c/c-parser.c.jj	2021-08-19 13:19:56.217288105 +0200
+++ gcc/c/c-parser.c	2021-08-19 15:22:10.146195464 +0200
@@ -1588,6 +1588,7 @@  static bool c_parser_omp_target (c_parse
 static void c_parser_omp_end_declare_target (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 bool c_parser_omp_ordered (c_parser *, enum pragma_context, bool *);
 static void c_parser_oacc_routine (c_parser *, enum pragma_context);
 
@@ -12485,6 +12486,9 @@  c_parser_pragma (c_parser *parser, enum
       c_parser_omp_nothing (parser);
       return false;
 
+    case PRAGMA_OMP_ERROR:
+      return c_parser_omp_error (parser, context);
+
     case PRAGMA_OMP_ORDERED:
       return c_parser_omp_ordered (parser, context, if_p);
 
@@ -21936,6 +21940,173 @@  c_parser_omp_nothing (c_parser *parser)
   c_parser_skip_to_pragma_eol (parser);
 }
 
+/* OpenMP 5.1
+   #pragma omp error clauses[optseq] new-line  */
+
+static bool
+c_parser_omp_error (c_parser *parser, enum pragma_context context)
+{
+  int at_compilation = -1;
+  int severity_fatal = -1;
+  tree message = NULL_TREE;
+  bool first = true;
+  bool bad = false;
+  location_t loc = c_parser_peek_token (parser)->location;
+
+  c_parser_consume_pragma (parser);
+
+  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;
+      static const char *args[] = {
+	"execution", "compilation", "warning", "fatal"
+      };
+      int *v = NULL;
+      int idx = 0, n = -1;
+      tree m = NULL_TREE;
+
+      if (!strcmp (p, "at"))
+	v = &at_compilation;
+      else if (!strcmp (p, "severity"))
+	{
+	  v = &severity_fatal;
+	  idx += 2;
+	}
+      else if (strcmp (p, "message"))
+	{
+	  error_at (cloc,
+		    "expected %<at%>, %<severity%> or %<message%> clause");
+	  c_parser_skip_to_pragma_eol (parser, false);
+	  return false;
+	}
+
+      c_parser_consume_token (parser);
+
+      matching_parens parens;
+      if (parens.require_open (parser))
+	{
+	  if (v == NULL)
+	    {
+	      location_t expr_loc = c_parser_peek_token (parser)->location;
+	      c_expr expr = c_parser_expr_no_commas (parser, NULL);
+	      expr = convert_lvalue_to_rvalue (expr_loc, expr, true, true);
+	      m = convert (const_string_type_node, expr.value);
+	      m = c_fully_fold (m, false, NULL);
+	    }
+	  else
+	    {
+	      if (c_parser_next_token_is (parser, CPP_NAME))
+		{
+		  tree val = c_parser_peek_token (parser)->value;
+		  const char *q = IDENTIFIER_POINTER (val);
+
+		  if (!strcmp (q, args[idx]))
+		    n = 0;
+		  else if (!strcmp (q, args[idx + 1]))
+		    n = 1;
+		}
+	      if (n == -1)
+		{
+		  error_at (c_parser_peek_token (parser)->location,
+			    "expected %qs or %qs", args[idx], args[idx + 1]);
+		  bad = true;
+		  switch (c_parser_peek_token (parser)->type)
+		    {
+		    case CPP_EOF:
+		    case CPP_PRAGMA_EOL:
+		    case CPP_CLOSE_PAREN:
+		      break;
+		    default:
+		      if (c_parser_peek_2nd_token (parser)->type
+			  == CPP_CLOSE_PAREN)
+			c_parser_consume_token (parser);
+		      break;
+		    }
+		}
+	      else
+		c_parser_consume_token (parser);
+	    }
+
+	  parens.skip_until_found_close (parser);
+
+	  if (v == NULL)
+	    {
+	      if (message)
+		{
+		  error_at (cloc, "too many %qs clauses", p);
+		  bad = true;
+		}
+	      else
+		message = m;
+	    }
+	  else if (n != -1)
+	    {
+	      if (*v != -1)
+		{
+		  error_at (cloc, "too many %qs clauses", p);
+		  bad = true;
+		}
+	      else
+		*v = n;
+	    }
+	}
+      else
+	bad = true;
+    }
+  c_parser_skip_to_pragma_eol (parser);
+  if (bad)
+    return true;
+
+  if (at_compilation == -1)
+    at_compilation = 1;
+  if (severity_fatal == -1)
+    severity_fatal = 1;
+  if (!at_compilation)
+    {
+      if (context != pragma_compound)
+	{
+	  error_at (loc, "%<#pragma omp error%> with %<at(execution)%> clause "
+			 "may only be used in compound statements");
+	  return true;
+	}
+      tree fndecl
+	= builtin_decl_explicit (severity_fatal ? BUILT_IN_GOMP_ERROR
+						: BUILT_IN_GOMP_WARNING);
+      if (!message)
+	message = build_zero_cst (const_string_type_node);
+      tree stmt = build_call_expr_loc (loc, fndecl, 2, message,
+				       build_all_ones_cst (size_type_node));
+      add_stmt (stmt);
+      return true;
+    }
+  const char *msg = NULL;
+  if (message)
+    {
+      msg = c_getstr (message);
+      if (msg == NULL)
+	msg = _("<message unknown at compile time>");
+    }
+  if (msg)
+    emit_diagnostic (severity_fatal ? DK_ERROR : DK_WARNING, loc, 0,
+		     "%<pragma omp error%> encountered: %s", msg);
+  else
+    emit_diagnostic (severity_fatal ? DK_ERROR : DK_WARNING, loc, 0,
+		     "%<pragma omp error%> encountered");
+  return false;
+}
+
 /* Main entry point to parsing most OpenMP pragmas.  */
 
 static void
--- gcc/cp/parser.c.jj	2021-08-19 13:40:26.387178416 +0200
+++ gcc/cp/parser.c	2021-08-19 17:47:20.769883975 +0200
@@ -11760,10 +11760,30 @@  cp_parser_handle_statement_omp_attribute
 				    "depend") == 0)
 		  kind = C_OMP_DIR_STANDALONE;
 	      }
-	    /* else if (dir->id == PRAGMA_OMP_ERROR)
+	    else if (dir->id == PRAGMA_OMP_ERROR)
 	      {
-		error with at(execution) clause is C_OMP_DIR_STANDALONE.
-	      }  */
+		/* error with at(execution) clause is C_OMP_DIR_STANDALONE.  */
+		int paren_depth = 0;
+		for (int i = 1; first + i < last; i++)
+		  if (first[i].type == CPP_OPEN_PAREN)
+		    paren_depth++;
+		  else if (first[i].type == CPP_CLOSE_PAREN)
+		    paren_depth--;
+		  else if (paren_depth == 0
+			   && first + i + 2 < last
+			   && first[i].type == CPP_NAME
+			   && first[i + 1].type == CPP_OPEN_PAREN
+			   && first[i + 2].type == CPP_NAME
+			   && !strcmp (IDENTIFIER_POINTER (first[i].u.value),
+				       "at")
+			   && !strcmp (IDENTIFIER_POINTER (first[i
+								 + 2].u.value),
+				       "execution"))
+		    {
+		      kind = C_OMP_DIR_STANDALONE;
+		      break;
+		    }
+	      }
 	    cp_omp_attribute_data v = { DEFPARSE_TOKENS (d), dir, kind };
 	    vec.safe_push (v);
 	    if (flag_openmp || dir->simd)
@@ -45590,6 +45610,184 @@  cp_parser_omp_nothing (cp_parser *parser
 }
 
 
+/* OpenMP 5.1
+   #pragma omp error clauses[optseq] new-line  */
+
+static bool
+cp_parser_omp_error (cp_parser *parser, cp_token *pragma_tok,
+		     enum pragma_context context)
+{
+  int at_compilation = -1;
+  int severity_fatal = -1;
+  tree message = NULL_TREE;
+  bool first = true;
+  bool bad = false;
+  location_t loc = pragma_tok->location;
+
+  while (cp_lexer_next_token_is_not (parser->lexer, CPP_PRAGMA_EOL))
+    {
+      /* For now only in C++ attributes, do it always for OpenMP 5.1.  */
+      if ((!first || parser->lexer->in_omp_attribute_pragma)
+	  && cp_lexer_next_token_is (parser->lexer, CPP_COMMA)
+	  && cp_lexer_nth_token_is (parser->lexer, 2, CPP_NAME))
+	cp_lexer_consume_token (parser->lexer);
+
+      first = false;
+
+      if (cp_lexer_next_token_is_not (parser->lexer, CPP_NAME))
+	break;
+
+      const char *p
+	= IDENTIFIER_POINTER (cp_lexer_peek_token (parser->lexer)->u.value);
+      location_t cloc = cp_lexer_peek_token (parser->lexer)->location;
+      static const char *args[] = {
+	"execution", "compilation", "warning", "fatal"
+      };
+      int *v = NULL;
+      int idx = 0, n = -1;
+      tree m = NULL_TREE;
+
+      if (!strcmp (p, "at"))
+	v = &at_compilation;
+      else if (!strcmp (p, "severity"))
+	{
+	  v = &severity_fatal;
+	  idx += 2;
+	}
+      else if (strcmp (p, "message"))
+	{
+	  error_at (cloc,
+		    "expected %<at%>, %<severity%> or %<message%> clause");
+	  cp_parser_skip_to_pragma_eol (parser, pragma_tok);
+	  return false;
+	}
+
+      cp_lexer_consume_token (parser->lexer);
+
+      matching_parens parens;
+      if (parens.require_open (parser))
+	{
+	  if (v == NULL)
+	    {
+	      m = cp_parser_assignment_expression (parser);
+	      if (type_dependent_expression_p (m))
+		m = build1 (IMPLICIT_CONV_EXPR, const_string_type_node, m);
+	      else
+		m = perform_implicit_conversion_flags (const_string_type_node, m,
+						       tf_warning_or_error,
+						       LOOKUP_NORMAL);
+	    }
+	  else
+	    {
+	      if (cp_lexer_next_token_is (parser->lexer, CPP_NAME))
+		{
+		  tree val = cp_lexer_peek_token (parser->lexer)->u.value;
+		  const char *q = IDENTIFIER_POINTER (val);
+
+		  if (!strcmp (q, args[idx]))
+		    n = 0;
+		  else if (!strcmp (q, args[idx + 1]))
+		    n = 1;
+		}
+	      if (n == -1)
+		{
+		  error_at (cp_lexer_peek_token (parser->lexer)->location,
+			    "expected %qs or %qs", args[idx], args[idx + 1]);
+		  bad = true;
+		  switch (cp_lexer_peek_token (parser->lexer)->type)
+		    {
+		    case CPP_EOF:
+		    case CPP_PRAGMA_EOL:
+		    case CPP_CLOSE_PAREN:
+		      break;
+		    default:
+		      if (cp_lexer_nth_token_is (parser->lexer, 2,
+						 CPP_CLOSE_PAREN))
+			cp_lexer_consume_token (parser->lexer);
+		      break;
+		    }
+		}
+	      else
+		cp_lexer_consume_token (parser->lexer);
+	    }
+
+	  if (!parens.require_close (parser))
+	    cp_parser_skip_to_closing_parenthesis (parser,
+						   /*recovering=*/true,
+						   /*or_comma=*/false,
+						   /*consume_paren=*/
+						   true);
+
+	  if (v == NULL)
+	    {
+	      if (message)
+		{
+		  error_at (cloc, "too many %qs clauses", p);
+		  bad = true;
+		}
+	      else
+		message = m;
+	    }
+	  else if (n != -1)
+	    {
+	      if (*v != -1)
+		{
+		  error_at (cloc, "too many %qs clauses", p);
+		  bad = true;
+		}
+	      else
+		*v = n;
+	    }
+	}
+      else
+	bad = true;
+    }
+  cp_parser_require_pragma_eol (parser, pragma_tok);
+  if (bad)
+    return true;
+
+  if (at_compilation == -1)
+    at_compilation = 1;
+  if (severity_fatal == -1)
+    severity_fatal = 1;
+  if (!at_compilation)
+    {
+      if (context != pragma_compound)
+	{
+	  error_at (loc, "%<#pragma omp error%> with %<at(execution)%> clause "
+			 "may only be used in compound statements");
+	  return true;
+	}
+      tree fndecl
+	= builtin_decl_explicit (severity_fatal ? BUILT_IN_GOMP_ERROR
+						: BUILT_IN_GOMP_WARNING);
+      if (!message)
+	message = build_zero_cst (const_string_type_node);
+      tree stmt = build_call_expr_loc (loc, fndecl, 2, message,
+				       build_all_ones_cst (size_type_node));
+      add_stmt (stmt);
+      return true;
+    }
+
+  if (in_discarded_stmt)
+    return false;
+
+  const char *msg = NULL;
+  if (message)
+    {
+      msg = c_getstr (fold_for_warn (message));
+      if (msg == NULL)
+	msg = _("<message unknown at compile time>");
+    }
+  if (msg)
+    emit_diagnostic (severity_fatal ? DK_ERROR : DK_WARNING, loc, 0,
+		     "%<pragma omp error%> encountered: %s", msg);
+  else
+    emit_diagnostic (severity_fatal ? DK_ERROR : DK_WARNING, loc, 0,
+		     "%<pragma omp error%> encountered");
+  return false;
+}
+
 /* OpenMP 4.5:
    #pragma omp taskloop taskloop-clause[optseq] new-line
      for-loop
@@ -46703,6 +46901,9 @@  cp_parser_pragma (cp_parser *parser, enu
       cp_parser_omp_nothing (parser, pragma_tok);
       return false;
 
+    case PRAGMA_OMP_ERROR:
+      return cp_parser_omp_error (parser, pragma_tok, context);
+
     case PRAGMA_OMP_ORDERED:
       if (context != pragma_stmt && context != pragma_compound)
 	goto bad_stmt;
--- gcc/fortran/types.def.jj	2021-08-17 09:24:58.778051205 +0200
+++ gcc/fortran/types.def	2021-08-19 17:47:10.548026356 +0200
@@ -120,6 +120,7 @@  DEF_FUNCTION_TYPE_2 (BT_FN_BOOL_INT_BOOL
 DEF_FUNCTION_TYPE_2 (BT_FN_VOID_UINT_UINT, BT_VOID, BT_UINT, BT_UINT)
 DEF_FUNCTION_TYPE_2 (BT_FN_VOID_PTR_PTRMODE,
 		     BT_VOID, BT_PTR, BT_PTRMODE)
+DEF_FUNCTION_TYPE_2 (BT_FN_VOID_CONST_PTR_SIZE, BT_VOID, BT_CONST_PTR, BT_SIZE)
 
 DEF_POINTER_TYPE (BT_PTR_FN_VOID_PTR_PTR, BT_FN_VOID_PTR_PTR)
 
--- gcc/fortran/f95-lang.c.jj	2021-08-19 12:53:44.693106618 +0200
+++ gcc/fortran/f95-lang.c	2021-08-19 14:37:58.004167196 +0200
@@ -535,7 +535,7 @@  gfc_builtin_function (tree decl)
   return decl;
 }
 
-/* So far we need just these 8 attribute types.  */
+/* So far we need just these 10 attribute types.  */
 #define ATTR_NULL			0
 #define ATTR_LEAF_LIST			(ECF_LEAF)
 #define ATTR_NOTHROW_LEAF_LIST		(ECF_NOTHROW | ECF_LEAF)
@@ -546,6 +546,9 @@  gfc_builtin_function (tree decl)
 #define ATTR_CONST_NOTHROW_LIST		(ECF_NOTHROW | ECF_CONST)
 #define ATTR_ALLOC_WARN_UNUSED_RESULT_SIZE_2_NOTHROW_LIST \
 					(ECF_NOTHROW)
+#define ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST \
+					(ECF_COLD | ECF_NORETURN | \
+					 ECF_NOTHROW | ECF_LEAF)
 
 static void
 gfc_define_builtin (const char *name, tree type, enum built_in_function code,
--- gcc/testsuite/c-c++-common/gomp/error-1.c.jj	2021-08-19 14:37:58.000167251 +0200
+++ gcc/testsuite/c-c++-common/gomp/error-1.c	2021-08-19 14:37:57.999167265 +0200
@@ -0,0 +1,45 @@ 
+#pragma omp error			/* { dg-error "'pragma omp error' encountered" } */
+#pragma omp error at(compilation)	/* { dg-error "'pragma omp error' encountered" } */
+#pragma omp error severity(fatal)	/* { dg-error "'pragma omp error' encountered" } */
+#pragma omp error message("my msg")	/* { dg-error "'pragma omp error' encountered: my msg" } */
+#pragma omp error severity(warning)message("another message")at(compilation)	/* { dg-warning "'pragma omp error' encountered: another message" } */
+
+struct S {
+  #pragma omp error			/* { dg-error "'pragma omp error' encountered" } */
+  #pragma omp error at(compilation)	/* { dg-error "'pragma omp error' encountered" } */
+  #pragma omp error severity(fatal)	/* { dg-error "'pragma omp error' encountered" } */
+  #pragma omp error message("42")	/* { dg-error "'pragma omp error' encountered: 42" } */
+  #pragma omp error severity(warning), message("foo"), at(compilation)	/* { dg-warning "'pragma omp error' encountered: foo" } */
+  int s;
+};
+
+int
+foo (int i, int x)
+{
+  #pragma omp error			/* { dg-error "'pragma omp error' encountered" } */
+  #pragma omp error at(compilation)	/* { dg-error "'pragma omp error' encountered" } */
+  #pragma omp error severity(fatal)	/* { dg-error "'pragma omp error' encountered" } */
+  #pragma omp error message("42 / 1")	/* { dg-error "'pragma omp error' encountered: 42 / 1" } */
+  #pragma omp error severity(warning) message("bar") at(compilation)	/* { dg-warning "'pragma omp error' encountered: bar" } */
+  if (x)
+    #pragma omp error			/* { dg-error "'pragma omp error' encountered" } */
+    i++;
+  if (x)
+    ;
+  else
+    #pragma omp error at(compilation)	/* { dg-error "'pragma omp error' encountered" } */
+    i++;
+  switch (0)
+    #pragma omp error severity(fatal)	/* { dg-error "'pragma omp error' encountered" } */
+    {
+    default:
+      break;
+    }
+  while (0)
+    #pragma omp error message("42 - 1")	/* { dg-error "'pragma omp error' encountered: 42 - 1" } */
+    i++;
+  lab:
+  #pragma omp error severity(warning) message("bar") at(compilation)	/* { dg-warning "'pragma omp error' encountered: bar" } */
+    i++;
+  return i;
+}
--- gcc/testsuite/c-c++-common/gomp/error-2.c.jj	2021-08-19 14:37:58.000167251 +0200
+++ gcc/testsuite/c-c++-common/gomp/error-2.c	2021-08-19 14:37:58.000167251 +0200
@@ -0,0 +1,24 @@ 
+void
+foo (int x, const char *msg1, const char *msg2)
+{
+  if (x == 0)
+    {
+      #pragma omp error at(execution)
+    }
+  else if (x == 1)
+    {
+      #pragma omp error severity (warning), at (execution)
+    }
+  else if (x == 2)
+    {
+      #pragma omp error at ( execution ) severity (fatal) message ("baz")
+    }
+  else if (x == 3)
+    {
+      #pragma omp error severity(warning) message (msg1) at(execution)
+    }
+  else
+    {
+      #pragma omp error message (msg2), at(execution), severity(fatal)
+    }
+}
--- gcc/testsuite/c-c++-common/gomp/error-3.c.jj	2021-08-19 14:37:58.000167251 +0200
+++ gcc/testsuite/c-c++-common/gomp/error-3.c	2021-08-19 17:41:51.712467429 +0200
@@ -0,0 +1,70 @@ 
+#pragma omp error asdf				/* { dg-error "expected 'at', 'severity' or 'message' clause" } */
+#pragma omp error at				/* { dg-error "expected '\\\(' before end of line" } */
+#pragma omp error at(				/* { dg-error "expected 'execution' or 'compilation'" } */
+						/* { dg-error "expected '\\\)' before end of line" "" { target *-*-* } .-1 } */
+#pragma omp error at(runtime)			/* { dg-error "expected 'execution' or 'compilation'" } */
+#pragma omp error at(+				/* { dg-error "expected 'execution' or 'compilation'" } */
+						/* { dg-error "expected '\\\)' before '\\\+' token" "" { target *-*-* } .-1 } */
+#pragma omp error at(compilation		/* { dg-error "expected '\\\)' before end of line" } */
+						/* { dg-error "'pragma omp error' encountered" "" { target *-*-* } .-1 } */
+#pragma omp error severity			/* { dg-error "expected '\\\(' before end of line" } */
+#pragma omp error severity(			/* { dg-error "expected 'warning' or 'fatal'" } */
+						/* { dg-error "expected '\\\)' before end of line" "" { target *-*-* } .-1 } */
+#pragma omp error severity(error)		/* { dg-error "expected 'warning' or 'fatal'" } */
+#pragma omp error severity(-			/* { dg-error "expected 'warning' or 'fatal'" } */
+						/* { dg-error "expected '\\\)' before '-' token" "" { target *-*-* } .-1 } */
+#pragma omp error severity(fatal		/* { dg-error "expected '\\\)' before end of line" } */
+						/* { dg-error "'pragma omp error' encountered" "" { target *-*-* } .-1 } */
+#pragma omp error message			/* { dg-error "expected '\\\(' before end of line" } */
+#pragma omp error message(			/* { dg-error "expected expression before end of line" "" { target c } } */
+						/* { dg-error "expected primary-expression before end of line" "" { target c++ } .-1 } */
+						/* { dg-error "expected '\\\)' before end of line" "" { target c++ } .-2 } */
+						/* { dg-error "'pragma omp error' encountered: <message unknown at compile time>" "" { target *-*-* } .-3 } */
+#pragma omp error message(0			/* { dg-error "expected '\\\)' before end of line" } */
+						/* { dg-error "'pragma omp error' encountered: <message unknown at compile time>" "" { target *-*-* } .-1 } */
+#pragma omp error message("foo"			/* { dg-error "expected '\\\)' before end of line" } */
+						/* { dg-error "'pragma omp error' encountered: foo" "" { target *-*-* } .-1 } */
+#pragma omp error message(1)			/* { dg-error "'pragma omp error' encountered: <message unknown at compile time>" } */
+						/* { dg-error "invalid conversion from 'int' to 'const char\\*'" "" { target c++ } .-1 } */
+#pragma omp error message(1.2)			/* { dg-error "cannot convert to a pointer type" "" { target c } } */
+						/* { dg-error "could not convert" "" { target c++ } .-1 } */
+						/* { dg-error "'pragma omp error' encountered: <message unknown at compile time>" "" { target *-*-* } .-2 } */
+#pragma omp error message(L"bar")		/* { dg-error "'pragma omp error' encountered: <message unknown at compile time>" } */
+						/* { dg-error "could not convert" "" { target c++ } .-1 } */
+#pragma omp error message("foo"),at(compilation),severity(fatal),	/* { dg-error "expected end of line before ',' token" } */
+						/* { dg-error "'pragma omp error' encountered: foo" "" { target *-*-* } .-1 } */
+#pragma omp error message("foo"),at(compilation),severity(fatal),asdf	/* { dg-error "expected 'at', 'severity' or 'message' clause" } */
+#pragma omp error at(compilation) at(compilation)	/* { dg-error "too many 'at' clauses" } */
+#pragma omp error severity(fatal) severity(warning)	/* { dg-error "too many 'severity' clauses" } */
+#pragma omp error message("foo") message("foo")		/* { dg-error "too many 'message' clauses" } */
+#pragma omp error at(execution)			/* { dg-error "'#pragma omp error' with 'at\\\(execution\\\)' clause may only be used in compound statements" } */
+
+struct S
+{
+  #pragma omp error at(execution) message("foo")/* { dg-error "'#pragma omp error' with 'at\\\(execution\\\)' clause may only be used in compound statements" } */
+  int s;
+};
+
+int
+foo (int i, int x, const char *msg)
+{
+  #pragma omp error message(msg)		/* { dg-error "'pragma omp error' encountered: <message unknown at compile time>" } */
+  if (x)
+    #pragma omp error at(execution)		/* { dg-error "'#pragma omp error' with 'at\\\(execution\\\)' clause may only be used in compound statements" } */
+  i++;
+  if (x)
+    ;
+  else
+    #pragma omp error at(execution) severity(warning)	/* { dg-error "'#pragma omp error' with 'at\\\(execution\\\)' clause may only be used in compound statements" } */
+  i++;
+  switch (0)
+    #pragma omp error severity(fatal) at(execution)	/* { dg-error "'#pragma omp error' with 'at\\\(execution\\\)' clause may only be used in compound statements" } */
+    ;
+  while (0)
+    #pragma omp error at(execution)message("42 - 1")	/* { dg-error "'#pragma omp error' with 'at\\\(execution\\\)' clause may only be used in compound statements" } */
+    i++;
+  lab:
+  #pragma omp error severity(warning) message("bar") at(execution)	/* { dg-error "'#pragma omp error' with 'at\\\(execution\\\)' clause may only be used in compound statements" } */
+    i++;
+  return i;
+}
--- gcc/testsuite/g++.dg/gomp/attrs-1.C.jj	2021-08-19 11:42:27.417421677 +0200
+++ gcc/testsuite/g++.dg/gomp/attrs-1.C	2021-08-19 17:05:23.953934117 +0200
@@ -109,9 +109,11 @@  baz (int d, int m, int i1, int i2, int p
 
 void
 bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
-     int nte, int tl, int nth, int g, int nta, int fi, int pp, int *q, int *dd, int ntm)
+     int nte, int tl, int nth, int g, int nta, int fi, int pp, int *q, int *dd, int ntm,
+     const char *msg)
 {
   [[omp::directive (nothing)]];
+  [[omp::directive (error at (execution) severity (warning) message (msg))]];
   [[omp::directive (for simd
     private (p) firstprivate (f) lastprivate (l) linear (ll:1) reduction(+:r) schedule(static, 4) collapse(1) nowait
     safelen(8) simdlen(4) aligned(q: 32) nontemporal(ntm) if(i1) order(concurrent) allocate (f))]]
--- gcc/testsuite/g++.dg/gomp/attrs-2.C.jj	2021-08-19 11:42:27.421421621 +0200
+++ gcc/testsuite/g++.dg/gomp/attrs-2.C	2021-08-19 17:05:06.757173496 +0200
@@ -109,9 +109,11 @@  baz (int d, int m, int i1, int i2, int p
 
 void
 bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
-     int nte, int tl, int nth, int g, int nta, int fi, int pp, int *q, int *dd, int ntm)
+     int nte, int tl, int nth, int g, int nta, int fi, int pp, int *q, int *dd, int ntm,
+     const char *msg)
 {
   [[omp::directive (nothing)]];
+  [[omp::directive (error, at (execution), severity (warning), message (msg))]];
   [[omp::directive (for simd,
     private (p),firstprivate (f),lastprivate (l),linear (ll:1),reduction(+:r),schedule(static, 4),collapse(1),nowait,
     safelen(8),simdlen(4),aligned(q: 32),nontemporal(ntm),if(i1),order(concurrent),allocate (f))]]
--- gcc/testsuite/g++.dg/gomp/attrs-13.C.jj	2021-08-19 17:09:01.228909636 +0200
+++ gcc/testsuite/g++.dg/gomp/attrs-13.C	2021-08-19 17:57:29.933397757 +0200
@@ -0,0 +1,34 @@ 
+// { dg-do compile { target c++11 } }
+
+[[omp::directive(error)]];			// { dg-error "'pragma omp error' encountered" }
+[[omp::directive(error, at(compilation))]];	// { dg-error "'pragma omp error' encountered" }
+[[omp::directive(error severity(fatal))]];	// { dg-error "'pragma omp error' encountered" }
+[[omp::directive(error, message("my msg"))]];	// { dg-error "'pragma omp error' encountered: my msg" }
+[[omp::directive(error severity(warning)message("another message")at(compilation))]];	// { dg-warning "'pragma omp error' encountered: another message" }
+
+int
+foo (int i, int x)
+{
+  [[omp::directive(error)]];			// { dg-error "'pragma omp error' encountered" }
+  [[omp::directive(error, at(compilation))]];	// { dg-error "'pragma omp error' encountered" }
+  [[omp::directive(error severity(fatal))]];	// { dg-error "'pragma omp error' encountered" }
+  [[omp::directive(error, message("42 / 1"))]];	// { dg-error "'pragma omp error' encountered: 42 / 1" }
+  [[omp::directive(error severity(warning) message("bar") at(compilation))]];	// { dg-warning "'pragma omp error' encountered: bar" }
+  if (x)
+    [[omp::directive(error)]];			// { dg-error "'pragma omp error' encountered" }
+  i++;
+  if (x)
+    ;
+  else
+    [[omp::directive(error at(compilation))]];	// { dg-error "'pragma omp error' encountered" }
+  i++;
+  switch (0)
+    [[omp::directive(error, severity(fatal))]];	// { dg-error "'pragma omp error' encountered" }
+  while (0)
+    [[omp::directive(error, message("42 - 1"))]];	// { dg-error "'pragma omp error' encountered: 42 - 1" }
+  i++;
+  lab:
+  [[omp::directive(error, severity(warning) message("bar"), at(compilation))]];	// { dg-warning "'pragma omp error' encountered: bar" }
+  i++;
+  return i;
+}
--- gcc/testsuite/g++.dg/gomp/error-1.C.jj	2021-08-19 17:18:58.231599327 +0200
+++ gcc/testsuite/g++.dg/gomp/error-1.C	2021-08-19 17:56:41.478073032 +0200
@@ -0,0 +1,42 @@ 
+// { dg-do compile { target c++17 } }
+
+void
+foo ()
+{
+  if constexpr (false)
+    {
+      #pragma omp error								// { dg-bogus "'pragma omp error' encountered" }
+    }
+  else
+    {
+      #pragma omp error at(compilation) severity(warning) message("foo")	// { dg-warning "'pragma omp error' encountered: foo" }
+    }
+  if constexpr (true)
+    {
+      #pragma omp error message("bar")						// { dg-error "'pragma omp error' encountered: bar" }
+    }
+  else
+    {
+      #pragma omp error message("baz")						// { dg-bogus "'pragma omp error' encountered" }
+    }
+}
+
+template <typename T>
+bool
+bar (T x)
+{
+  #pragma omp error at(execution) message (x)
+  return false;
+}
+
+bool a = bar ("foo");
+
+template <typename T>
+bool
+baz (T x)
+{
+  #pragma omp error at(execution) message (x)					// { dg-error "could not convert" }
+  return false;
+}
+
+bool b = baz (L"foo");
--- libgomp/libgomp.map.jj	2021-08-19 12:53:44.693106618 +0200
+++ libgomp/libgomp.map	2021-08-19 14:37:58.004167196 +0200
@@ -382,7 +382,9 @@  GOMP_5.0.1 {
 
 GOMP_5.1 {
   global:
+	GOMP_error;
 	GOMP_scope_start;
+	GOMP_warning;
 } GOMP_5.0.1;
 
 OACC_2.0 {
--- libgomp/libgomp_g.h.jj	2021-08-19 12:53:44.693106618 +0200
+++ libgomp/libgomp_g.h	2021-08-19 14:58:44.728786591 +0200
@@ -366,6 +366,11 @@  extern void GOMP_teams_reg (void (*) (vo
 extern void *GOMP_alloc (size_t, size_t, uintptr_t);
 extern void GOMP_free (void *, uintptr_t);
 
+/* error.c */
+
+extern void GOMP_warning (const char *, size_t);
+extern void GOMP_error (const char *, size_t);
+
 /* oacc-async.c */
 
 extern void GOACC_wait (int, int, ...);
--- libgomp/error.c.jj	2021-08-19 12:53:44.693106618 +0200
+++ libgomp/error.c	2021-08-19 17:58:55.633203432 +0200
@@ -89,3 +89,34 @@  gomp_fatal (const char *fmt, ...)
   gomp_vfatal (fmt, list);
   va_end (list);
 }
+
+void
+GOMP_warning (const char *msg, size_t msglen)
+{
+  if (msg && msglen == (size_t) -1)
+    gomp_error ("error directive encountered: %s", msg);
+  else if (msg)
+    {
+      fputs ("\nlibgomp: error directive encountered: ", stderr);
+      fwrite (msg, 1, msglen, stderr);
+      fputc ('\n', stderr);
+    }
+  else
+    gomp_error ("error directive encountered");
+}
+
+void
+GOMP_error (const char *msg, size_t msglen)
+{
+  if (msg && msglen == (size_t) -1)
+    gomp_fatal ("fatal error: error directive encountered: %s", msg);
+  else if (msg)
+    {
+      fputs ("\nlibgomp: fatal error: error directive encountered: ", stderr);
+      fwrite (msg, 1, msglen, stderr);
+      fputc ('\n', stderr);
+      exit (EXIT_FAILURE);
+    }
+  else
+    gomp_fatal ("fatal error: error directive encountered");
+}
--- libgomp/testsuite/libgomp.c-c++-common/error-1.c.jj	2021-08-19 15:26:12.576816311 +0200
+++ libgomp/testsuite/libgomp.c-c++-common/error-1.c	2021-08-19 17:59:10.389997780 +0200
@@ -0,0 +1,49 @@ 
+/* { dg-shouldfail "error directive" } */
+
+#ifdef __cplusplus
+extern "C"
+#endif
+void abort ();
+
+int
+foo (int i, int x)
+{
+  if (x)
+    #pragma omp error severity(warning)	/* { dg-warning "'pragma omp error' encountered" } */
+    i++;
+  if (!x)
+    ;
+  else
+    #pragma omp error severity(warning)	/* { dg-warning "'pragma omp error' encountered" } */
+    i += 2;
+  switch (0)
+    #pragma omp error severity(warning)	/* { dg-warning "'pragma omp error' encountered" } */
+    {
+    default:
+      break;
+    }
+  while (0)
+    #pragma omp error message("42 - 1")	severity (warning) /* { dg-warning "'pragma omp error' encountered: 42 - 1" } */
+    i += 4;
+  lab:
+  #pragma omp error severity(warning) message("bar") at(compilation)	/* { dg-warning "'pragma omp error' encountered: bar" } */
+    i += 8;
+  return i;
+}
+
+int
+main ()
+{
+  if (foo (5, 0) != 13 || foo (6, 1) != 17)
+    abort ();
+  #pragma omp error at (execution) severity (warning)
+  const char *msg = "my message" + 2;
+  #pragma omp error at (execution) severity (warning) message (msg + 1)
+  #pragma omp error at (execution) severity (fatal) message (msg - 2)
+  #pragma omp error at (execution) severity (warning) message ("foobar")
+  return 0;
+}
+
+/* { dg-output "libgomp: error directive encountered(\n|\r|\n\r)(\n|\r|\n\r)" } */
+/* { dg-output "libgomp: error directive encountered: message(\n|\r|\n\r)(\n|\r|\n\r)" } */
+/* { dg-output "libgomp: fatal error: error directive encountered: my message" } */