This is an archive of the discontinued LLVM Phabricator instance.

[flang][OpenMP] Added semantic checks for sections (associated section(s) should be structured block(s)) and simd constructs (associated loop(s) should be structured block(s))
ClosedPublic

Authored by NimishMishra on Aug 29 2021, 10:18 PM.

Details

Summary

According to OpenMP 5.0 spec document, the following semantic restrictions have been dealt with in this patch.

  1. [sections] Orphaned section directives are prohibited. That is, the section directives must appear within the sections construct and must not be encountered elsewhere in the sections region.

    Semantic checks for the following are not necessary, since use of orphaned section construct (i.e. without an enclosing sections directive) throws parser errors and control flow never reaches the semantic checking phase.

Added a test case for the same in llvm-project/flang/test/Parser/omp-sections01.f90

  1. [sections] Must be a structured block

Added test case as llvm-project/flang/test/Semantics/omp-sections02.f90 and made changes to branching logic in llvm-project/flang/lib/Semantics/check-directive-structure.h as follows:

  • NoBranchingEnforce is used within a parser::Walk called from CheckNoBranching (in check-directive-structure.h itself). CheckNoBranching is invoked from a lot of places, but of our interest in this particular context is void OmpStructureChecker::Enter(const parser::OpenMPSectionsConstruct &x) (defined within llvm-project/flang/lib/Semantics/check-omp-structure.cpp)
  • [addition] NoBranchingEnforce lacked tracking context earlier (precisely once control flow entered a OpenMP directive, constructs were no longer being recorded on the ConstructStack). Relevant Pre and Post functions added for the same
  • [addition] A EmitBranchOutError on encountering a CALL in a structured block
  • [changes] Although NoBranchingEnforce was handling labelled EXITs and CYCLEs well, there were no semantic checks for unlabelled CYCLEs and EXITs. Support added for them as well. For unlabeled cycles and exits, there's a need to efficiently differentiate the ConstructNode added to the stack before the concerned OpenMP directive was encountered and the nodes added after the directive was encountered. The same is done through a simple private unsigned counter within NoBranchingEnforce itself
  • [addition] Addition of EmitUnlabelledBranchOutError is due to lack of an error message that gave proper context to the programmer. Existing error messages are either named error messages (which aren't valid for unlabeled cycles and exits) or a simple CYCLE/EXIT statement not allowed within SECTIONS construct which is more generic than the reality.

Other than these implementations, a test case for simd construct has been added.

  1. [Test case for simd] Must be a structured block / A program that branches in or out of a function with declare simd is non conforming

Uses the already existing branching out of OpenMP structured block logic as well as changes introduced in point 3 below to semantically validate the restriction. Test case added to llvm-project/flang/test/Semantics/omp-simd01.f90


High priority TODOs:

  1. (done) Decide whether CALL is an invalid branch out of an OpenMP structured block
  2. (done) Fix !$omp do's handling of unlabeled CYCLEs

Diff Detail

Event Timeline

NimishMishra created this revision.Aug 29 2021, 10:18 PM
NimishMishra created this object with edit policy "Administrators".
NimishMishra requested review of this revision.Aug 29 2021, 10:18 PM
NimishMishra changed the edit policy from "Administrators" to "All Users".
NimishMishra changed the edit policy from "All Users" to "Administrators".

git-clang-format-7 changes

NimishMishra edited the summary of this revision. (Show Details)Aug 30 2021, 11:37 AM

Fixed !$omp do's handling of unlabeled CYCLEs

NimishMishra changed the edit policy from "Administrators" to "All Users".Sep 2 2021, 4:55 AM
kiranchandramohan requested changes to this revision.Sep 2 2021, 4:58 AM

Thanks @NimishMishra for this patch. I have left some comments.

Could you change the title of the review to explicitly say which semantic check is being implemented?
Can you change the description to first summarize what is being implemented, how it is implemented and then say what is left/TODO?

flang/lib/Semantics/check-directive-structure.h
33

Can this be along with the other private variables in the bottom of this class?

36

I think this can just be an unsigned integer. While we might only require a smaller integer, I don't think we use such smaller integers elsewhere.

76

Nit: braced initialization.

flang/test/Parser/omp-sections01.f90
11–16

Is the point here that the failure of parsing indicates that orphaned section directives are not permitted?
I think we should have proper error messages instead of a regular parsing failure.

flang/test/Semantics/omp-simd01.f90
1–2

This test is probably not working as expected. Can you change to using test_errors.py? Please consult similar existing tests in this directory.

23

Spelling: Invalid

flang/lib/Semantics/check-directive-structure.h
35

Spelling: encountering

52

Would using a separate stack be better? Ideally, we should not modify the context stack. If in case this is better then there should be asserts to ensure that the stack is left in the same situation at the end.

63

CALL is OK in a structured block.
Remove commented code.

76–77

Nit: This comment is not required I think.

77

Why is the != check required? When is it equal to x ("DO")?
The comment below is not required.

81–83

These comments are also not required.

103

Would it be possible to make this error similar to the error in EmitBranchOutErrorWithName?

flang/test/Parser/omp-sections01.f90
2

This test is probably not working as expected. Can you change to using test_errors.py? Please consult similar existing tests in this directory.

flang/test/Semantics/omp-sections02.f90
2

This test is probably not working as expected. Can you change to using test_errors.py? Please consult similar existing tests in this directory.

This revision now requires changes to proceed.Sep 2 2021, 4:58 AM
NimishMishra marked 14 inline comments as done.Sep 2 2021, 2:56 PM
NimishMishra added inline comments.
flang/lib/Semantics/check-directive-structure.h
52

I did not use the private stack because since we are already receiving the context as a SemanticsContext& through the class constructor, I thought to use to services of stack push and pop wrapper functions that class SemanticsContext offers.

Check added to ensure the context stack is in the same situation at the end as it was at the beginning.

76

Replaced this with a better switch-case construct

77

For some directives (for example omp simd or omp do), there has to be a DO following the directive. The parser makes sure of that.

So it doesn't make sense to check unlabeled cycles for such special directives because the parser makes sure we have a matched DO that came after the directive was encountered in the source.

However, the string comparison was sloppy. I was replaced it with a finer switch-case with scope to add more such directives in the future.

flang/test/Parser/omp-sections01.f90
11–16

I had a talk with @kiranktp and he opined that the Parser is woefully sloppy with the context it gives to the error messages as well as is in itself complex to change those messages in the first place.

We discussed to return to this Parser's contextual error message issue sometime later. So for now, it's marked as a TODO and the test-case itself is XFAIL

NimishMishra marked 3 inline comments as done.
NimishMishra retitled this revision from [flang][OpenMP] Added semantic checks for sections and simd constructs to [flang][OpenMP] Added semantic checks for sections (associated section(s) should be structured block(s)) and simd constructs (associated loop(s) should be structured block(s)).
NimishMishra edited the summary of this revision. (Show Details)
NimishMishra marked an inline comment as done.Sep 2 2021, 3:01 PM

I think your implementation does deal with the first point in your summary. You only add one test case, right?

flang/lib/Semantics/check-directive-structure.h
149

Instead of giving these comments. Could you please give one comment such as CheckConstructNameBranching(const char *stmt, const parser::Name &stmtName) to differentiate their usage? Maybe this advice is not good.

flang/test/Semantics/omp-sections02.f90
23

Add space before err=30. Also please format your test cases. I think fortran test cases use two spaces on the next line instead of four spaces like the style in your part of code in omp-simd01.f90.

flang/test/Semantics/omp-simd01.f90
12

Does your implementation support the following code:

!$omp simd
do i = 1, 10
  30 stop
  do j = 1, 10
    goto 30
  end do
end do
!$omp end simd.

If no collapse clause specified, it behaves as if only one loop immediately following is associated with the construct. Could you please add the test in your test case?

I think your implementation does deal with the first point in your summary. You only add one test case, right?

According to the spec document, I had to deal with orphaned section (without an enclosing sections). I added a test case for the same. However, presently, the parser itself is dealing correctly with the issue by giving errors with very less contextual information. Somewhere down the line, we may need to shift this check from the Parser to Semantics, or improve error reporting in the Parser itself. For now, the test case is a XFAIL with a TODO added to it.

flang/lib/Semantics/check-directive-structure.h
149

I added the comments for easier review and understanding of the changes. Now since there is no problem with the approach taken, I too think I should reduce the verbosity.

flang/test/Semantics/omp-sections02.f90
23

I will do this. When the discussion in other comments is resolved, will submit another diff with formatted test cases

flang/test/Semantics/omp-simd01.f90
12

The present state of the implementation gives a 'STOP statement is not allowed in a SIMD construct'. Is that what you were looking for?

If not, this was added as void Post(const parser::StopStmt &) { EmitBranchOutError("STOP"); } in NoBranchingEnforce (source: https://reviews.llvm.org/D88655). Possibly @kiranchandramohan can offer some insights. According to his comment there, parallel region can have a STOP. I am not sure about SIMD.

That said, if I replace that STOP statement with something else (like the example below), no errors are generated. That is expected I believe since branching within SIMD's associated DO loop shouldn't be considered a violation of the structured block restriction.

program sample                                                                                                                                                 use omp_lib                                                                                                                                                !$omp simd                                                                                                                                                   do i = 1, 10                                                                                                                                                 30 print*, "sample"                                                                                                                                          do j = 1, 10                                                                                                                                                   goto 30                                                                                                                                                      end do                                                                                                                                                     end do                                                                                                                                                     !$omp end simd                                                                                                                             end program sample
peixin added a comment.Sep 3 2021, 9:04 AM

According to the spec document, I had to deal with orphaned section (without an enclosing sections). I added a test case for the same. However, presently, the parser itself is dealing correctly with the issue by giving errors with very less contextual information. Somewhere down the line, we may need to shift this check from the Parser to Semantics, or improve error reporting in the Parser itself. For now, the test case is a XFAIL with a TODO added to it.

I understand this. What I mean is your implementation is not consistent with the summary, which should be fixed since this patch has not implement the semantic check with informative error message. Otherwise, the summary, which should be the commit message, is confusing.

flang/test/Semantics/omp-simd01.f90
12

You can replace it with print *, "sample". My key point is to check branching out to the nested loop which is not associated with simd construct. I thought out one more better test case as follows:

!$omp simd collapse(2)
do i = 1, 10
  30 print *, "This is wrong"
  do j = 1, 10
    40 print *, "This is OK"
    do k = 1, 10
      if (expression) then
        goto 30
      end if
      if (expression2) then
        goto 40
      end if
    end do
  end do
end do
!$omp end simd

If my understanding is correct, please polish this test case and add it.

NimishMishra added inline comments.Sep 5 2021, 11:16 PM
flang/test/Semantics/omp-simd01.f90
12

The constraint is there, albeit not because of my changes. When I run this case, it gives a The value of the parameter in the COLLAPSE or ORDERED clause must not be larger than the number of nested loops following the construct.

The reason for this, as it seems to me is, that when we give a COLLAPSE(2), it checks for immediately nesting of exactly 2 DO constructs. Anything else simply raises this error. In this vein, the following modification of this test case succeeds:

!$omp simd collapse(2)
do i = 1, 10  ! Removed 30 print*, ...
  do j = 1, 10
    40 print *, "This is OK"
    do k = 1, 10
      if (expression) then
        goto 30
      end if
      if (expression2) then
        goto 40
      end if
    end do
  end do
end do
!$omp end simd

Similarly, trying COLLAPSE(3) would force removal of the print statement at label 40. So I think we can be reasonably assured that the simd nesting checks implemented in check-omp-structure.cpp will assure absence of any such statements which can violate the structured block restriction.

Please let me know your thoughts on this.

peixin added inline comments.Sep 6 2021, 4:01 AM
flang/test/Semantics/omp-simd01.f90
12

Thanks for the investigation. OK to me now.

kiranchandramohan requested changes to this revision.Sep 14 2021, 4:13 AM

As requested previously, Could you change the Summary of this PR to

-> What is implemented in this patch.
-> What is not required because it is covered by parsing.
-> What is TODO.
-> Any questions.

flang/lib/Semantics/check-directive-structure.h
43

Is this copy required if we are using a privateStack? The privateStack will contain the new Constructs. I think the numPrivateConstructs variable is also not required. We can also remove the assert below.

46

I don't think we use assert in the Frontend code. It could be DIE that we use. Please check.

The change above might not be required if we don't copy the construct stack.

131

Do you need the counter if you are using a private stack?

161

If only used here then you can inline the function.

flang/test/Semantics/omp-simd01.f90
1–2

Please use test_errors.py. You might need a rebase. OK to delay it till you submit it. Remove the REQUIRES shell line.

12

STOP is allowed in OpenMP structured blocks for Fortran.

This revision now requires changes to proceed.Sep 14 2021, 4:13 AM
NimishMishra edited the summary of this revision. (Show Details)
NimishMishra marked 11 inline comments as done.Sep 25 2021, 12:31 AM
NimishMishra added inline comments.
flang/lib/Semantics/check-directive-structure.h
43

Removed everything. Now I have one private vector and handle everything there only. Also removed the counter

46

Since I am no longer making a local copy, so this asset as well as the class destructor is no longer there

131

No. I have removed that now

161

Yes. Inlined the function

flang/test/Semantics/omp-sections02.f90
23

Used https://github.com/pseewald/fprettify to format all fortran source.

flang/test/Semantics/omp-simd01.f90
1–2

Shifted from using test_errors.sh. Now the test cases use test_error.py

NimishMishra edited the summary of this revision. (Show Details)Sep 25 2021, 12:35 AM
NimishMishra updated this revision to Diff 375108.
NimishMishra marked 5 inline comments as done.
kiranchandramohan requested changes to this revision.Sep 27 2021, 2:39 PM

A few more questions and comments.

flang/lib/Semantics/check-directive-structure.h
19

Nit: Accidentally removed?

126–127

Wouldn't the removal of the reference result in a copy? Is that the intention here?

140

CheckForRequiredConstruct -> CheckForRequiredDoConstruct or CheckForDoConstruct

154

This function can be a lambda.

166

Looking at this again, do we need a construct stack? Can we just maintain a DoConstruct counter (which increments on entering (or Pre) a Do and decrements on leaving (or Post) a Do)? And if the DoConstruct counter value is greater than 0 then we are inside a DoConstruct, else outside.

This revision now requires changes to proceed.Sep 27 2021, 2:39 PM
NimishMishra added inline comments.Sep 27 2021, 9:00 PM
flang/lib/Semantics/check-directive-structure.h
19

No I don't think so. I did add #include<assert.h> when I included that assert statement. When that statement was removed, this include was also removed. I think a space may have stayed there, and got removed this time during formatting.

126–127

This is a slip from my end. I did not intend it. Will add the reference back

140

Carrying out this discussion in the other comment thread as well: on there being a counter instead of a stack

166

Yes it can be. However, note that this class NoBranchingEnforce is used by a parser::Walk within CheckNoBranching function whose aim is to check non-essential branching within OpenMP/OpenACC constructs.

Do you think, in foresight, that this private stack approach would be better than the counter approach? Just in case there may come up a use for some other construct as we expand our OpenMP/OpenACC support.

Similar thought I had for the introduced CheckForRequiredConstruct. As we expand, it could serve as the point of call from various other functions/places aiding them to make a decision on when to throw the illegal branching error.

flang/lib/Semantics/check-directive-structure.h
166

In the past, there have been instances where we have replaced with a more general data-structure. If you can point out a particular case where this will be required then let us keep the construct stack and add a comment saying this will be useful when we implement that case. Otherwise, let us replace it with a counter.

Can you quickly glance through the standard and have a quick check whether there are such checks required? From the top of my head, I don't see anything but I could have missed something.

NimishMishra marked an inline comment as done.Sep 29 2021, 10:46 PM
NimishMishra added inline comments.
flang/lib/Semantics/check-directive-structure.h
19

Please disregard the above comment. This was due to my editor's configuration. I will revert this space back as it was before.

154

With the counter approach, I think this won't be necessary anymore.

166

I searched through the standard. As far as I can see, I found a few other examples where this could be useful but none of them were without DoConstruct.

So I think I will remove this stack approach now and replace it with a private counter.

LGTM. Thanks @NimishMishra for this patch, and addressing all the comments, and for your patience.

I have two Nits. Please have a look, fix if necessary and you can submit. You might have to trim the commit message.

flang/lib/Semantics/check-directive-structure.h
169

Nit: An underscore at the end to match others?

flang/test/Semantics/omp-simd01.f90
48

Nit: Are these ">" format characters. Can you check?

This revision is now accepted and ready to land.Sep 30 2021, 9:34 AM
NimishMishra marked 7 inline comments as done.Sep 30 2021, 10:44 AM

Thanks a lot @kiranchandramohan and @peixin for suggestions on the patch.

flang/lib/Semantics/check-directive-structure.h
169

Yes will add that.

flang/test/Semantics/omp-simd01.f90
48

I used fprettify (https://github.com/pseewald/fprettify) to format the test cases. So yes this is indentation added by the same.

NimishMishra marked 2 inline comments as done.Sep 30 2021, 10:47 AM

@NimishMishra I believe this patch is causing a false semantic error with SNAP. The error being:

error: Semantic errors in inner.f90
./inner.f90:152:7: error: CYCLE to construct outside of PARALLEL DO construct is not allowed
        IF ( g == 0 ) CYCLE

The code in snap it is erroring at in inner.f90 is:

!$OMP PARALLEL DO NUM_THREADS(nnstd_used) IF(nnstd_used>1)           &
!$OMP& SCHEDULE(STATIC,1) DEFAULT(SHARED) PRIVATE(n,g)               &
!$OMP& PROC_BIND(CLOSE)
  DO n = 1, ng_per_thrd
    g = grp_act(n,t)
    IF ( g == 0 ) CYCLE
    CALL inner_df_calc ( inno, iits(g), flux0pi(:,:,:,g),            &
                      flux0(:,:,:,g), dfmxi(g) )
  END DO
!$OMP END PARALLEL DO

AFAIK this should be valid code, and I know that @Leporacanthicus is also experiencing this issue (although it looks like there could be a secondary issue as well).
I've tried reverting this patch, and managed to stop getting this error when using the flang-omp-report plugin.

@NimishMishra I believe this patch is causing a false semantic error with SNAP. The error being:

error: Semantic errors in inner.f90
./inner.f90:152:7: error: CYCLE to construct outside of PARALLEL DO construct is not allowed
        IF ( g == 0 ) CYCLE

The code in snap it is erroring at in inner.f90 is:

!$OMP PARALLEL DO NUM_THREADS(nnstd_used) IF(nnstd_used>1)           &
!$OMP& SCHEDULE(STATIC,1) DEFAULT(SHARED) PRIVATE(n,g)               &
!$OMP& PROC_BIND(CLOSE)
  DO n = 1, ng_per_thrd
    g = grp_act(n,t)
    IF ( g == 0 ) CYCLE
    CALL inner_df_calc ( inno, iits(g), flux0pi(:,:,:,g),            &
                      flux0(:,:,:,g), dfmxi(g) )
  END DO
!$OMP END PARALLEL DO

AFAIK this should be valid code, and I know that @Leporacanthicus is also experiencing this issue (although it looks like there could be a secondary issue as well).
I've tried reverting this patch, and managed to stop getting this error when using the flang-omp-report plugin.

@josh.mottley.arm Thanks for reporting. I triaged this. It turns out the particular class that is enforcing the semantic checks assumes the OpenMP constructs to be passed to it. In case of looping constructs however, a parser::Walk is called within OpenMP constructs rather than on them. I have provided a fix in https://github.com/llvm/llvm-project/commit/9faed889cfebf5d77faf1fab1ef8f0a2f0255e5c. Please see if this resolves your problems. I also edited a relevant test file to reflect this CYCLE condition. It wasn't there before.

@NimishMishra Thanks for the help. Looks like the patch fixes the issue I was having on my end.