[OpenMP,5.1,Fortran] Strictly-structured block support for OpenMP directives

Message ID 8d20877d-d52e-d90c-8a4e-a38f43921df1@codesourcery.com
State New
Headers
Series [OpenMP,5.1,Fortran] Strictly-structured block support for OpenMP directives |

Commit Message

Chung-Lin Tang Oct. 7, 2021, 1:59 p.m. UTC
  Hi all,
this patch add support for "strictly-structured blocks" introduced in OpenMP 5.1,
basically allowing BLOCK constructs to serve as the body for directives:

!$omp target
block
   ...
end block
[!$omp end target]  !! end directive is optional

!$omp parallel
block
   ...
end block
...
!$omp end parallel  !! error, considered as not match to above parallel directive

The parsing loop in parse_omp_structured_block() has been modified to allow
a BLOCK construct after the first statement has been detected to be ST_BLOCK.
This is done by a hard modification of the state into (the new) COMP_OMP_STRICTLY_STRUCTURED_BLOCK
after the statement is known (I'm not sure if there's a way to 'peek' the next
statement/token in the Fortran FE, open to suggestions on how to better write this)

Tested with no regressions on trunk, is this okay to commit?

Thanks,
Chung-Lin

2021-10-07  Chung-Lin Tang  <cltang@codesourcery.com>

gcc/fortran/ChangeLog:

	* decl.c (gfc_match_end): Add COMP_OMP_STRICTLY_STRUCTURED_BLOCK case
	together with COMP_BLOCK.
	* parse.c (parse_omp_structured_block): Adjust declaration, add
	'bool strictly_structured_block' default true parameter, add handling
	for strictly-structured block case, adjust recursive calls to
	parse_omp_structured_block.
	(parse_executable): Adjust calls to parse_omp_structured_block.
	* parse.h (enum gfc_compile_state): Add
	COMP_OMP_STRICTLY_STRUCTURED_BLOCK.
	* trans-openmp.c (gfc_trans_omp_workshare): Add EXEC_BLOCK case
	handling.

gcc/testsuite/ChangeLog:

	* gfortran.dg/gomp/strictly-structured-block-1.f90: New test.
  

Comments

Tobias Burnus Oct. 7, 2021, 4:33 p.m. UTC | #1
Hi Chung-Lin,

On 07.10.21 15:59, Chung-Lin Tang wrote:
> this patch add support for "strictly-structured blocks" introduced in
> OpenMP 5.1,
> basically allowing BLOCK constructs to serve as the body for directives:
>
> !$omp target
> block
>   ...
> end block
> [!$omp end target]  !! end directive is optional

Pre-remark: That OpenMP feature causes ambiguities.
I have filled an OpenMP spec issue to discuss this, Issue 3154.

Namely the following is unclear:

!$omp parallel
  !$omp parallel
   block
     x=  x+  1
   end block
  !$omp end parallel

Does the 'end parallel' end the inner strictly structured block
or the outer loosely structured block?

In principle, a compiler could defer this until later during
parsing, but this requires a tremendous state tracking and it
is surely not simple to do in gfortran (and probably all other
compilers). — Thus, I think the spec will change and probably
in the way which this patch has implemented.

NOTE: It takes the kind of directive into account, i.e.
'omp end target' vs. 'omp end parallel' aren't ambiguous.


> The parsing loop in parse_omp_structured_block() has been modified to
> allow
> a BLOCK construct after the first statement has been detected to be
> ST_BLOCK.
> This is done by a hard modification of the state into (the new)
> COMP_OMP_STRICTLY_STRUCTURED_BLOCK
> after the statement is known (I'm not sure if there's a way to 'peek'
> the next
> statement/token in the Fortran FE, open to suggestions on how to
> better write this)
>
> Tested with no regressions on trunk, is this okay to commit?

LGTM  – unless Jakub has further comments.

However, I have two requests:

First, can you include an update of the implementation status
in libgomp/libgomp.texi (+ fix the ...ppp... typo):

"@item Suppport of strictly structured blocks in Fortran @tab N @tab"


Secondly, can you extend the testcase a bit to include
nesting a BLOCK inside the other BLOCK – and nesting
the directive with a strictly structured block inside
another (different) directive to ensure that the
'omp end ...' is correctly matched.

I mean something like:

integer :: x
!$omp target map(i)
  !$omp parallel
   block
     block
      x = x + 1
     end block
   end block
!$omp end target

!$omp target map(i)
  !$omp parallel
   block
     block
      x = x + 1
     end block
   end block
   !$omp end parallel
!$omp end target
end

Thanks,

Tobias

> 2021-10-07 Chung-Lin Tang  <cltang@codesourcery.com>
>
> gcc/fortran/ChangeLog:
>
>     * decl.c (gfc_match_end): Add COMP_OMP_STRICTLY_STRUCTURED_BLOCK case
>     together with COMP_BLOCK.
>     * parse.c (parse_omp_structured_block): Adjust declaration, add
>     'bool strictly_structured_block' default true parameter, add handling
>     for strictly-structured block case, adjust recursive calls to
>     parse_omp_structured_block.
>     (parse_executable): Adjust calls to parse_omp_structured_block.
>     * parse.h (enum gfc_compile_state): Add
>     COMP_OMP_STRICTLY_STRUCTURED_BLOCK.
>     * trans-openmp.c (gfc_trans_omp_workshare): Add EXEC_BLOCK case
>     handling.
>
> gcc/testsuite/ChangeLog:
>
>     * gfortran.dg/gomp/strictly-structured-block-1.f90: New test.
-----------------
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 Oct. 7, 2021, 5:09 p.m. UTC | #2
On Thu, Oct 07, 2021 at 09:59:00PM +0800, Chung-Lin Tang wrote:
> this patch add support for "strictly-structured blocks" introduced in OpenMP 5.1,
> basically allowing BLOCK constructs to serve as the body for directives:
> 
> !$omp target
> block
>   ...
> end block
> [!$omp end target]  !! end directive is optional
> 
> !$omp parallel
> block
>   ...
> end block
> ...
> !$omp end parallel  !! error, considered as not match to above parallel directive
> 
> The parsing loop in parse_omp_structured_block() has been modified to allow
> a BLOCK construct after the first statement has been detected to be ST_BLOCK.
> This is done by a hard modification of the state into (the new) COMP_OMP_STRICTLY_STRUCTURED_BLOCK
> after the statement is known (I'm not sure if there's a way to 'peek' the next
> statement/token in the Fortran FE, open to suggestions on how to better write this)

Thanks for working on this.
The workshare/parallel workshare case is unclear, I've filed
https://github.com/OpenMP/spec/issues/3153
for it.  Either don't allow block if workshare_stmts_only for now
until that is clarified, or if we do, we need to make sure that it does the
expected thing, does that gfc_trans_block_construct call ensure it?

Then we have the
https://github.com/OpenMP/spec/issues/3154
issue Tobias discovered, if that issue is resolved to end always applying to
the directive before the block statement, I think your patch handles it that
way but we want testsuite coverage for some of those cases.

For the testcases, I think best would be to split it into two, one that
contains only what we want to accept and another one with dg-errors in it.

I don't think the patch does the right thing for sections/parallel sections.
That is (at least in 5.1) defined as:
!$omp sections clauses...
[!$omp section]
structured-block-sequence
[!$omp section
structured-block-sequence]
...
!$omp end sections
(and similarly for parallel sections).
I believe your patch properly disallows:
!$omp sections
block
...
!$omp section
...
end block
!$omp end sections
- block itself is allowed, e.g.
!$omp sections
block
a=1
b=2
end block
!$omp end sections
with the meaning that the block is after the first implied !$omp section
and there is nothing else.  But does the patch actually check that
!$omp sections
block
...
end block
c=1
!$omp end sections
or
!$omp sections
!$omp section
block
...
end block
c=1
!$omp section
d=1
!$omp end sections
is invalid?  Though, not sure if that was the intended effect, in
OpenMP 5.0 that used to be fine.  But then the other changes are backwards
incompatible too,
!$omp parallel
block
  ...
end block
c=1
!$omp end parallel
used to be valid but no longer is.  structured-block-sequence is fortran
defined as structured-block and structured-block is defined as either
loosely-structured-block or strictly-structured-block, so for sections
in between each !$omp section should be either anything not starting with
block, or, if it starts with block, after end block there should be
immediately !$omp section or !$omp end {,parallel }sections.

Another thing is scan, the wording is similar and newly
!$omp do reduction(+,inscan:a)
do i=1,10
block
  ...
end block
x=1
!$omp scan
block
  ...
end block
x=2
end do
is invalid.

> @@ -5538,6 +5539,32 @@ parse_omp_structured_block (gfc_statement omp_st, bool workshare_stmts_only)
>        gcc_unreachable ();
>      }
>  
> +  bool block_construct = false;
> +  gfc_namespace* my_ns = NULL;
> +  gfc_namespace* my_parent = NULL;

The usual coding conventions put * before variable name instead of after it
(except for libstdc++).

> --- /dev/null
> +++ b/gcc/testsuite/gfortran.dg/gomp/strictly-structured-block-1.f90
> @@ -0,0 +1,295 @@
> +! { dg-do compile }
> +! { dg-options "-fopenmp" }
> +
> +program main
> +  integer :: x
> +
> +  !$omp parallel
> +  block
> +    x = x + 1
> +  end block
> +
> +  !$omp parallel
> +  block
> +    x = x + 1
> +  end block
> +  !$omp end parallel
> +
> +  !$omp parallel
> +  block
> +    x = x + 1
> +  end block
> +  x = x + 1
> +  !$omp end parallel ! { dg-error "Unexpected !.OMP END PARALLEL statement" }

Other than the splitting into non-dg-error stuff in one testcase and
dg-error in another one, I think we want (probably in yet another pair of
testcases) test what we are not required to do but with your patch we
actually implement, in particular that !$omp master behaves the same way.

> +  !$omp ordered
> +  block
> +    x = x + 1
> +  end block
> +
> +  !$omp ordered
> +  block
> +    x = x + 1
> +  end block
> +  !$omp end ordered
> +
> +  !$omp ordered
> +  block
> +    x = x + 1
> +  end block
> +  x = x + 1
> +  !$omp end ordered ! { dg-error "Unexpected !.OMP END ORDERED statement" }

I believe these 3 can't be done in the program, would either need to be
wrapped in some !$omp do with ordered clause or should go each ordered into
its own orphaned subroutine, because ordered region must bind to a
worksharing-loop with ordered clause.  I think wrapping it inside of !$omp
do ordered is easier.

	Jakub
  
Jakub Jelinek Oct. 14, 2021, 10:20 a.m. UTC | #3
On Thu, Oct 07, 2021 at 07:09:07PM +0200, Jakub Jelinek wrote:
> The workshare/parallel workshare case is unclear, I've filed
> https://github.com/OpenMP/spec/issues/3153
> for it.  Either don't allow block if workshare_stmts_only for now
> until that is clarified, or if we do, we need to make sure that it does the
> expected thing, does that gfc_trans_block_construct call ensure it?
> 
> Then we have the
> https://github.com/OpenMP/spec/issues/3154
> issue Tobias discovered, if that issue is resolved to end always applying to
> the directive before the block statement, I think your patch handles it that
> way but we want testsuite coverage for some of those cases.

Just want to follow-up on this, we now have resolutions of the
https://github.com/OpenMP/spec/issues/3153
https://github.com/OpenMP/spec/issues/3154
https://github.com/OpenMP/spec/issues/3155
issues and we can use that to guide this patch.
BLOCK is now explicitly allowed for workshare around the body of
workshare/parallel workshare or around the body of critical in it but not
arbitrarily nested.  My understanding of the patch is that it most likely
implements that, just we need a testsuite coverage that
!$omp workshare
block
  a = 1
  b = 2
  !$omp critical
  block
    c = 3
  end block
end block
is fine (also with !$omp end {criticial,workshare} after the block), but
that
!$omp workshare
a = 1
block
  b = 2
  c = 3
end block
!$omp end workshare
etc. is diagnosed.
For Tobias' issue that !$omp end whatever after end block for strictly
structured block binds to the directive above the strictly structured block
I think the patch also implements it but we want again testsuite coverage,
that
subroutine foo
!$omp parallel
!$omp parallel
block
end block
!$omp end parallel
!$omp end parallel
end subroutine foo
subroutine bar
!$omp teams
!$omp parallel
block
end block
!$omp end teams
end subroutine bar
is fine while e.g.
subroutine baz
!$omp parallel
!$omp parallel
block
end block
!$omp end parallel
end subroutine baz
is not (!$omp end parallel pairs with the inner parallel rather than outer,
and the outer parallel's body doesn't start with BLOCK, so needs to be
paired with its !$omp end parallel).
And lastly, the 3rd ticket clarifies that for the separating directives
for Fortran basically the 5.0 state remains except that the body can be now
also optionally wrapped in a single BLOCK.
(And for C/C++ allows no statements at all in between the separating
directives or after/before them but still requires the {}s around it like
5.1 and earlier.  Here we implement the 5.1 wording and let's stay with
that.)
Thinking more about the Fortran case for !$omp sections, there is an
ambiguity.
!$omp sections
block
  !$omp section
end block
is clear and !$omp end sections is optional, but
!$omp sections
block
end block
is ambiguous during parsing, it could be either followed by !$omp section
and then the BLOCK would be first section, or by !$omp end sections and then
it would be clearly the whole sections, with first section being empty
inside of the block, or if it is followed by something else, it is
ambiguous whether the block ... end block is part of the first section,
followed by something and then we should be looking later for either
!$omp section or !$omp end section to prove that, or if
!$omp sections
block
end block
was the whole sections construct and we shouldn't await anything further.
I'm afraid back to the drawing board.

	Jakub
  
Jakub Jelinek Oct. 14, 2021, 11:19 a.m. UTC | #4
On Thu, Oct 14, 2021 at 12:20:51PM +0200, Jakub Jelinek via Gcc-patches wrote:
> Thinking more about the Fortran case for !$omp sections, there is an
> ambiguity.
> !$omp sections
> block
>   !$omp section
> end block
> is clear and !$omp end sections is optional, but
> !$omp sections
> block
> end block
> is ambiguous during parsing, it could be either followed by !$omp section
> and then the BLOCK would be first section, or by !$omp end sections and then
> it would be clearly the whole sections, with first section being empty
> inside of the block, or if it is followed by something else, it is
> ambiguous whether the block ... end block is part of the first section,
> followed by something and then we should be looking later for either
> !$omp section or !$omp end section to prove that, or if
> !$omp sections
> block
> end block
> was the whole sections construct and we shouldn't await anything further.
> I'm afraid back to the drawing board.

And I have to correct myself, there is no ambiguity in 5.2 here,
the important fact is hidden in sections/parallel sections being
block-associated constructs.  That means the body of the whole construct
has to be a structured-block, and by the 5.1+ definition of Fortran
structured block, it is either block ... end block or something that
doesn't start with block.
So,
!$omp sections
block
end block
a = 1
is only ambiguous in whether it is actually
!$omp sections
block
  !$omp section
end block
a = 1
or
!$omp sections
!$omp section
block
end block
!$omp end sections
a = 1
but both actually do the same thing, work roughly as !$omp single.
If one wants block statement as first in structured-block-sequence
of the first section, followed by either some further statements
or by other sections, then one needs to write
!$omp sections
!$omp section
block
end block
a = 1
...
!$omp end sections
or
!$omp sections
block
  block
  end block
  a = 1
...
end block

Your patch probably already handles it that way, but we again need
testsuite coverage to prove it is handled the way it should in all these
cases (and that we diagnose what is invalid).

	Jakub
  
Chung-Lin Tang Oct. 15, 2021, 6:44 p.m. UTC | #5
On 2021/10/14 7:19 PM, Jakub Jelinek wrote:
> On Thu, Oct 14, 2021 at 12:20:51PM +0200, Jakub Jelinek via Gcc-patches wrote:
>> Thinking more about the Fortran case for !$omp sections, there is an
>> ambiguity.
>> !$omp sections
>> block
>>    !$omp section
>> end block
>> is clear and !$omp end sections is optional, but
>> !$omp sections
>> block
>> end block
>> is ambiguous during parsing, it could be either followed by !$omp section
>> and then the BLOCK would be first section, or by !$omp end sections and then
>> it would be clearly the whole sections, with first section being empty
>> inside of the block, or if it is followed by something else, it is
>> ambiguous whether the block ... end block is part of the first section,
>> followed by something and then we should be looking later for either
>> !$omp section or !$omp end section to prove that, or if
>> !$omp sections
>> block
>> end block
>> was the whole sections construct and we shouldn't await anything further.
>> I'm afraid back to the drawing board.
> 
> And I have to correct myself, there is no ambiguity in 5.2 here,
> the important fact is hidden in sections/parallel sections being
> block-associated constructs.  That means the body of the whole construct
> has to be a structured-block, and by the 5.1+ definition of Fortran
> structured block, it is either block ... end block or something that
> doesn't start with block.
> So,
> !$omp sections
> block
> end block
> a = 1
> is only ambiguous in whether it is actually
> !$omp sections
> block
>    !$omp section
> end block
> a = 1
> or
> !$omp sections
> !$omp section
> block
> end block
> !$omp end sections
> a = 1
> but both actually do the same thing, work roughly as !$omp single.
> If one wants block statement as first in structured-block-sequence
> of the first section, followed by either some further statements
> or by other sections, then one needs to write
> !$omp sections
> !$omp section
> block
> end block
> a = 1
> ...
> !$omp end sections
> or
> !$omp sections
> block
>    block
>    end block
>    a = 1
> ...
> end block
> 
> Your patch probably already handles it that way, but we again need
> testsuite coverage to prove it is handled the way it should in all these
> cases (and that we diagnose what is invalid).

The patch currently does not allow strictly-structured BLOCK for sections/parallel sections,
since I was referencing the 5.1 spec while writing it, although that is trivially fixable.
(was sensing a bit odd why those two constructs had to be specially treated in 5.1 anyways)

The bigger issue is that under the current way the patch is written, the statements inside
a [parallel] sections construct are parsed automatically by parse_executable(), so to enforce
the specified meaning of "structured-block-sequence" (i.e. BLOCK or non-BLOCK starting sequence of stmts)
will probably be more a bit harder to implement:

!$omp sections
block
    !$omp section
    block
      x=0
    end block
    x=1           !! This is allowed now, though should be wrong spec-wise
    !$omp section
    x=2
end block

Currently "$!omp section" acts essentially as a top-level separator within a sections-construct,
rather than a structured directive. Though I would kind of argue this is actually better to use for the
user (why prohibit what looks like very apparent meaning of the program?)

So Jakub, my question for this is, is this current state okay? Or must we implement the spec pedantically?

As for the other issues:
(1) BLOCK/END BLOCK is not generally handled in parse_omp_structured_block, so for workshare,
     it is only handled for the top-level construct, not within workshare. I think this is what you meant
     in the last mail.

(2) As for the dangling-!$omp_end issue Tobias raised, because we are basically using 1-statement lookahead,
     any "!$omp end <*>" is naturally bound with the adjacent BLOCK/END BLOCK, so we should be okay there.

Thanks,
Chung-Lin
  
Jakub Jelinek Oct. 15, 2021, 7:02 p.m. UTC | #6
On Sat, Oct 16, 2021 at 02:44:12AM +0800, Chung-Lin Tang wrote:
> The patch currently does not allow strictly-structured BLOCK for sections/parallel sections,
> since I was referencing the 5.1 spec while writing it, although that is trivially fixable.
> (was sensing a bit odd why those two constructs had to be specially treated in 5.1 anyways)
> 
> The bigger issue is that under the current way the patch is written, the statements inside
> a [parallel] sections construct are parsed automatically by parse_executable(), so to enforce
> the specified meaning of "structured-block-sequence" (i.e. BLOCK or non-BLOCK starting sequence of stmts)
> will probably be more a bit harder to implement:
> 
> !$omp sections
> block
>    !$omp section
>    block
>      x=0
>    end block
>    x=1           !! This is allowed now, though should be wrong spec-wise
>    !$omp section
>    x=2
> end block
> 
> Currently "$!omp section" acts essentially as a top-level separator within a sections-construct,
> rather than a structured directive. Though I would kind of argue this is actually better to use for the
> user (why prohibit what looks like very apparent meaning of the program?)
> 
> So Jakub, my question for this is, is this current state okay? Or must we implement the spec pedantically?

I'd certainly not implement 5.1 pedantically when we know we'd change one
way for 5.0 -> 5.1 and change it back again for 5.1 -> 5.2.
An example of that is
!$omp sections
!$omp section
block
end block
x = 1
!$end omp sections
This is valid in 5.0 and will be valid again in 5.2 with the same meaning,
so let's just use the 5.0/5.2 wording here.  Ditto the !$omp end ambiguity
Tobias raised etc.
Whether to add support for the 5.2 behavior when one sees
!$omp {,parallel }sections
block
or not is more tough question, I bet the answer should be what is easier to
implement right now (i.e. don't spend too much effort on hard 5.1
implementation if 5.2 would be easier, as eventually we'd need to do the 5.2
implementation afterwards anyway).
So, for block right after sections either we implement the 5.2 wording right
away and therefore look for !$omp section only within that block and not
outside of it, or we for the 1st section with omitted !$omp section before
it only implement the 5.1 pedantic behavior, i.e. if it starts with a block,
don't look for !$omp section in the block, but require either !$omp section
or !$omp end sections right after the corresponding end block.

Does this answer all questions?

	Jakub
  

Patch

diff --git a/gcc/fortran/decl.c b/gcc/fortran/decl.c
index b3c65b7175b..ff66d1f9475 100644
--- a/gcc/fortran/decl.c
+++ b/gcc/fortran/decl.c
@@ -8445,6 +8445,7 @@  gfc_match_end (gfc_statement *st)
       break;
 
     case COMP_BLOCK:
+    case COMP_OMP_STRICTLY_STRUCTURED_BLOCK:
       *st = ST_END_BLOCK;
       target = " block";
       eos_ok = 0;
diff --git a/gcc/fortran/parse.c b/gcc/fortran/parse.c
index 7d765a0866d..d78bf9b8fa5 100644
--- a/gcc/fortran/parse.c
+++ b/gcc/fortran/parse.c
@@ -5451,8 +5451,9 @@  parse_oacc_loop (gfc_statement acc_st)
 
 /* Parse the statements of an OpenMP structured block.  */
 
-static void
-parse_omp_structured_block (gfc_statement omp_st, bool workshare_stmts_only)
+static gfc_statement
+parse_omp_structured_block (gfc_statement omp_st, bool workshare_stmts_only,
+			    bool strictly_structured_block = true)
 {
   gfc_statement st, omp_end_st;
   gfc_code *cp, *np;
@@ -5538,6 +5539,32 @@  parse_omp_structured_block (gfc_statement omp_st, bool workshare_stmts_only)
       gcc_unreachable ();
     }
 
+  bool block_construct = false;
+  gfc_namespace* my_ns = NULL;
+  gfc_namespace* my_parent = NULL;
+
+  st = next_statement ();
+
+  if (strictly_structured_block && st == ST_BLOCK)
+    {
+      /* Adjust state to a strictly-structured block, now that we found that
+	 the body starts with a BLOCK construct.  */
+      s.state = COMP_OMP_STRICTLY_STRUCTURED_BLOCK;
+
+      block_construct = true;
+      gfc_notify_std (GFC_STD_F2008, "BLOCK construct at %C");
+
+      my_ns = gfc_build_block_ns (gfc_current_ns);
+      gfc_current_ns = my_ns;
+      my_parent = my_ns->parent;
+
+      new_st.op = EXEC_BLOCK;
+      new_st.ext.block.ns = my_ns;
+      new_st.ext.block.assoc = NULL;
+      accept_statement (ST_BLOCK);
+      st = parse_spec (ST_NONE);
+    }
+
   do
     {
       if (workshare_stmts_only)
@@ -5554,7 +5581,6 @@  parse_omp_structured_block (gfc_statement omp_st, bool workshare_stmts_only)
 	     restrictions apply recursively.  */
 	  bool cycle = true;
 
-	  st = next_statement ();
 	  for (;;)
 	    {
 	      switch (st)
@@ -5576,17 +5602,20 @@  parse_omp_structured_block (gfc_statement omp_st, bool workshare_stmts_only)
 		  parse_forall_block ();
 		  break;
 
+		case ST_OMP_PARALLEL_SECTIONS:
+		  st = parse_omp_structured_block (st, false, false);
+		  continue;
+
 		case ST_OMP_PARALLEL:
 		case ST_OMP_PARALLEL_MASKED:
 		case ST_OMP_PARALLEL_MASTER:
-		case ST_OMP_PARALLEL_SECTIONS:
-		  parse_omp_structured_block (st, false);
-		  break;
+		  st = parse_omp_structured_block (st, false);
+		  continue;
 
 		case ST_OMP_PARALLEL_WORKSHARE:
 		case ST_OMP_CRITICAL:
-		  parse_omp_structured_block (st, true);
-		  break;
+		  st = parse_omp_structured_block (st, true);
+		  continue;
 
 		case ST_OMP_PARALLEL_DO:
 		case ST_OMP_PARALLEL_DO_SIMD:
@@ -5609,7 +5638,7 @@  parse_omp_structured_block (gfc_statement omp_st, bool workshare_stmts_only)
 	    }
 	}
       else
-	st = parse_executable (ST_NONE);
+	st = parse_executable (st);
       if (st == ST_NONE)
 	unexpected_eof ();
       else if (st == ST_OMP_SECTION
@@ -5619,9 +5648,27 @@  parse_omp_structured_block (gfc_statement omp_st, bool workshare_stmts_only)
 	  np = new_level (np);
 	  np->op = cp->op;
 	  np->block = NULL;
+	  st = next_statement ();
+	}
+      else if (block_construct && st == ST_END_BLOCK)
+	{
+	  accept_statement (st);
+	  gfc_current_ns = my_parent;
+	  pop_state ();
+
+	  st = next_statement ();
+	  if (st == omp_end_st)
+	    {
+	      accept_statement (st);
+	      st = next_statement ();
+	    }
+	  return st;
 	}
       else if (st != omp_end_st)
-	unexpected_statement (st);
+	{
+	  unexpected_statement (st);
+	  st = next_statement ();
+	}
     }
   while (st != omp_end_st);
 
@@ -5657,6 +5704,8 @@  parse_omp_structured_block (gfc_statement omp_st, bool workshare_stmts_only)
   gfc_commit_symbols ();
   gfc_warning_check ();
   pop_state ();
+  st = next_statement ();
+  return st;
 }
 
 
@@ -5779,16 +5828,19 @@  parse_executable (gfc_statement st)
 	  parse_oacc_structured_block (st);
 	  break;
 
+	case ST_OMP_PARALLEL_SECTIONS:
+	case ST_OMP_SECTIONS:
+	  st = parse_omp_structured_block (st, false, false);
+	  continue;
+
 	case ST_OMP_PARALLEL:
 	case ST_OMP_PARALLEL_MASKED:
 	case ST_OMP_PARALLEL_MASTER:
-	case ST_OMP_PARALLEL_SECTIONS:
 	case ST_OMP_ORDERED:
 	case ST_OMP_CRITICAL:
 	case ST_OMP_MASKED:
 	case ST_OMP_MASTER:
 	case ST_OMP_SCOPE:
-	case ST_OMP_SECTIONS:
 	case ST_OMP_SINGLE:
 	case ST_OMP_TARGET:
 	case ST_OMP_TARGET_DATA:
@@ -5797,13 +5849,13 @@  parse_executable (gfc_statement st)
 	case ST_OMP_TEAMS:
 	case ST_OMP_TASK:
 	case ST_OMP_TASKGROUP:
-	  parse_omp_structured_block (st, false);
-	  break;
+	  st = parse_omp_structured_block (st, false);
+	  continue;
 
 	case ST_OMP_WORKSHARE:
 	case ST_OMP_PARALLEL_WORKSHARE:
-	  parse_omp_structured_block (st, true);
-	  break;
+	  st = parse_omp_structured_block (st, true);
+	  continue;
 
 	case ST_OMP_DISTRIBUTE:
 	case ST_OMP_DISTRIBUTE_PARALLEL_DO:
diff --git a/gcc/fortran/parse.h b/gcc/fortran/parse.h
index 55f02299304..66b275de89b 100644
--- a/gcc/fortran/parse.h
+++ b/gcc/fortran/parse.h
@@ -31,7 +31,7 @@  enum gfc_compile_state
   COMP_STRUCTURE, COMP_UNION, COMP_MAP,
   COMP_DO, COMP_SELECT, COMP_FORALL, COMP_WHERE, COMP_CONTAINS, COMP_ENUM,
   COMP_SELECT_TYPE, COMP_SELECT_RANK, COMP_OMP_STRUCTURED_BLOCK, COMP_CRITICAL,
-  COMP_DO_CONCURRENT
+  COMP_DO_CONCURRENT, COMP_OMP_STRICTLY_STRUCTURED_BLOCK
 };
 
 /* Stack element for the current compilation state.  These structures
diff --git a/gcc/fortran/trans-openmp.c b/gcc/fortran/trans-openmp.c
index d234d1b070f..9fdea8c67fd 100644
--- a/gcc/fortran/trans-openmp.c
+++ b/gcc/fortran/trans-openmp.c
@@ -6993,7 +6993,11 @@  gfc_trans_omp_workshare (gfc_code *code, gfc_omp_clauses *clauses)
 	  res = gfc_trans_omp_directive (code);
 	  ompws_flags = saved_ompws_flags;
 	  break;
-	
+
+	case EXEC_BLOCK:
+	  res = gfc_trans_block_construct (code);
+	  break;
+
 	default:
 	  gfc_internal_error ("gfc_trans_omp_workshare(): Bad statement code");
 	}
diff --git a/gcc/testsuite/gfortran.dg/gomp/strictly-structured-block-1.f90 b/gcc/testsuite/gfortran.dg/gomp/strictly-structured-block-1.f90
new file mode 100644
index 00000000000..bc798c1c218
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/gomp/strictly-structured-block-1.f90
@@ -0,0 +1,295 @@ 
+! { dg-do compile }
+! { dg-options "-fopenmp" }
+
+program main
+  integer :: x
+
+  !$omp parallel
+  block
+    x = x + 1
+  end block
+
+  !$omp parallel
+  block
+    x = x + 1
+  end block
+  !$omp end parallel
+
+  !$omp parallel
+  block
+    x = x + 1
+  end block
+  x = x + 1
+  !$omp end parallel ! { dg-error "Unexpected !.OMP END PARALLEL statement" }
+
+  !$omp teams
+  block
+    x = x + 1
+  end block
+
+  !$omp teams
+  block
+    x = x + 1
+  end block
+  !$omp end teams
+
+  !$omp teams
+  block
+    x = x + 1
+  end block
+  x = x + 1
+  !$omp end teams ! { dg-error "Unexpected !.OMP END TEAMS statement" }
+
+  !$omp masked
+  block
+    x = x + 1
+  end block
+
+  !$omp masked
+  block
+    x = x + 1
+  end block
+  !$omp end masked
+
+  !$omp masked
+  block
+    x = x + 1
+  end block
+  x = x + 1
+  !$omp end masked ! { dg-error "Unexpected !.OMP END MASKED statement" }
+
+  !$omp scope
+  block
+    x = x + 1
+  end block
+
+  !$omp scope
+  block
+    x = x + 1
+  end block
+  !$omp end scope
+
+  !$omp scope
+  block
+    x = x + 1
+  end block
+  x = x + 1
+  !$omp end scope ! { dg-error "Unexpected !.OMP END SCOPE statement" }
+
+  !$omp single
+  block
+    x = x + 1
+  end block
+
+  !$omp single
+  block
+    x = x + 1
+  end block
+  !$omp end single
+
+  !$omp single
+  block
+    x = x + 1
+  end block
+  x = x + 1
+  !$omp end single ! { dg-error "Unexpected !.OMP END SINGLE statement" }
+
+  !$omp workshare
+  block
+    x = x + 1
+  end block
+
+  !$omp workshare
+  block
+    x = x + 1
+  end block
+  !$omp end workshare
+
+  !$omp workshare
+  block
+    x = x + 1
+  end block
+  x = x + 1
+  !$omp end workshare ! { dg-error "Unexpected !.OMP END WORKSHARE statement" }
+
+  !$omp task
+  block
+    x = x + 1
+  end block
+
+  !$omp task
+  block
+    x = x + 1
+  end block
+  !$omp end task
+
+  !$omp task
+  block
+    x = x + 1
+  end block
+  x = x + 1
+  !$omp end task ! { dg-error "Unexpected !.OMP END TASK statement" }
+
+  !$omp target data map(x)
+  block
+    x = x + 1
+  end block
+
+  !$omp target data map(x)
+  block
+    x = x + 1
+  end block
+  !$omp end target data
+
+  !$omp target data map(x)
+  block
+    x = x + 1
+  end block
+  x = x + 1
+  !$omp end target data ! { dg-error "Unexpected !.OMP END TARGET DATA statement" }
+
+  !$omp target
+  block
+    x = x + 1
+  end block
+
+  !$omp target
+  block
+    x = x + 1
+  end block
+  !$omp end target
+
+  !$omp target
+  block
+    x = x + 1
+  end block
+  x = x + 1
+  !$omp end target ! { dg-error "Unexpected !.OMP END TARGET statement" }
+
+  !$omp parallel workshare
+  block
+    x = x + 1
+  end block
+
+  !$omp parallel workshare
+  block
+    x = x + 1
+  end block
+  !$omp end parallel workshare
+
+  !$omp parallel workshare
+  block
+    x = x + 1
+  end block
+  x = x + 1
+  !$omp end parallel workshare ! { dg-error "Unexpected !.OMP END PARALLEL WORKSHARE statement" }
+
+  !$omp parallel masked
+  block
+    x = x + 1
+  end block
+
+  !$omp parallel masked
+  block
+    x = x + 1
+  end block
+  !$omp end parallel masked
+
+  !$omp parallel masked
+  block
+    x = x + 1
+  end block
+  x = x + 1
+  !$omp end parallel masked ! { dg-error "Unexpected !.OMP END PARALLEL MASKED statement" }
+
+  !$omp target parallel
+  block
+    x = x + 1
+  end block
+
+  !$omp target parallel
+  block
+    x = x + 1
+  end block
+  !$omp end target parallel
+
+  !$omp target parallel
+  block
+    x = x + 1
+  end block
+  x = x + 1
+  !$omp end target parallel ! { dg-error "Unexpected !.OMP END TARGET PARALLEL statement" }
+
+  !$omp target teams
+  block
+    x = x + 1
+  end block
+
+  !$omp target teams
+  block
+    x = x + 1
+  end block
+  !$omp end target teams
+
+  !$omp target teams
+  block
+    x = x + 1
+  end block
+  x = x + 1
+  !$omp end target teams ! { dg-error "Unexpected !.OMP END TARGET TEAMS statement" }
+
+  !$omp critical
+  block
+    x = x + 1
+  end block
+
+  !$omp critical
+  block
+    x = x + 1
+  end block
+  !$omp end critical
+
+  !$omp critical
+  block
+    x = x + 1
+  end block
+  x = x + 1
+  !$omp end critical ! { dg-error "Unexpected !.OMP END CRITICAL statement" }
+
+  !$omp taskgroup
+  block
+    x = x + 1
+  end block
+
+  !$omp taskgroup
+  block
+    x = x + 1
+  end block
+  !$omp end taskgroup
+
+  !$omp taskgroup
+  block
+    x = x + 1
+  end block
+  x = x + 1
+  !$omp end taskgroup ! { dg-error "Unexpected !.OMP END TASKGROUP statement" }
+
+  !$omp ordered
+  block
+    x = x + 1
+  end block
+
+  !$omp ordered
+  block
+    x = x + 1
+  end block
+  !$omp end ordered
+
+  !$omp ordered
+  block
+    x = x + 1
+  end block
+  x = x + 1
+  !$omp end ordered ! { dg-error "Unexpected !.OMP END ORDERED statement" }
+
+end program