[flang] [OpenMP] OmpVisitor framework for Name Resolution

This is a preliminary framework to do the name resolution for
data references on the OpenMP clauses. Unlike data references
in the OpenMP region, clauses determining the data-sharing or
data-mapping attributes are straightforward and the resolution
process could be extended to do the name resolution in the OpenMP
region. It is hard to determine what kind of checks can be done
in this visitor and what checks should be done later after name
resolution. But the guide line is that `After the completion of
this phase, every Name corresponds to a Symbol with proper OpenMP
attribute(s) determined unless an error occurred.`

1. Take data-sharing clauses as example, create new symbol for
variable that require private access within the OpenMP region.
Declare the entity implicitly if necessary. The new symbol has
`HostAssocDetails`, which is mentioned in the `OpenMP-semantics.md`.

2. For `Shared` or `ThreadPrivate`, no symbol needs to be created.
OpenMP attribute Flag `OmpThreadprivate` needs to be marked for
`Threadprivate` because the `threadprivate` attribute remains the
same whenever these variables are referenced in the program.
`Names` in `Shared` clause need to be resolved to associate the
symbols in the clause enclosing scope (contains the OpenMP directive)
but `OmpShared` does not need to be marked. Declare the entity
implicitly if necessary.

3. For `COMMON block`, when a named common block appears in a list,
it has the same meaning as if every explicit member of the common
block appeared in the list. Also, a common block name specified in
a data-sharing attribute clause must be declared to be a common
block in the same scoping unit in which the data-sharing attribute
clause appears. So, if a named common block appears on a `PRIVATE`
clause, all its members should have new symbols created within the
OpenMP region (scope). For later Semantic checks and CG, a new
symbol is also created for common block name with `HostAssocDetails`.

There are many things are still on the TODO list:
- Better error/warning messages with directive/clause source provenance

- Resolve variables referenced in the OpenMP region, for example,
  `private(tt%a)` is not allowed but `tt%a = 1` is allowed in the
  OpenMP region and a private version of `tt` maybe created for
  the region. The functions created in the `OmpVisitor` should be
  able to handle the name resolution on the statement too (more
  data structures may be introduced). This is a big portion and may
  require some interface changes to distinguish a reference is on
  `OpenMP directive/clause` or `statements within OpenMP region`.

- Same data reference appears on multiple data-sharing clauses.

- Take association into consideration for example Pointer association,
  `ASSOCIATE` construct, and etc.

- Handle `Array Sections` and `Array or Structure Element`.

- Handle all the name resolution for directives/clauses that have
  `parser::Name`.

- More tests

Original-commit: flang-compiler/f18@b2ea520885
This commit is contained in:
Jinxin Yang 2019-09-11 14:42:51 -07:00 committed by Jinxin (Brian) Yang
parent f6a5a3f45e
commit 4ca8c5dc5c
11 changed files with 528 additions and 4 deletions

View File

@ -929,7 +929,7 @@ private:
// Resolve construct entities and statement entities.
// Check that construct names don't conflict with other names.
class ConstructVisitor : public DeclarationVisitor {
class ConstructVisitor : public virtual DeclarationVisitor {
public:
bool Pre(const parser::ConcurrentHeader &);
bool Pre(const parser::LocalitySpec::Local &);
@ -1037,11 +1037,201 @@ private:
void PopAssociation();
};
static const parser::Name *GetDesignatorNameIf(
const parser::Designator &designator) {
const auto *dataRef{std::get_if<parser::DataRef>(&designator.u)};
return dataRef ? std::get_if<parser::Name>(&dataRef->u) : nullptr;
}
static constexpr Symbol::Flags ompFlagsRequireNewSymbol{
Symbol::Flag::OmpPrivate, Symbol::Flag::OmpLinear,
Symbol::Flag::OmpFirstPrivate, Symbol::Flag::OmpLastPrivate,
Symbol::Flag::OmpReduction};
static constexpr Symbol::Flags ompFlagsRequireMark{
Symbol::Flag::OmpThreadprivate};
// Resolve OpenMP construct entities and statement (TODO) entities
class OmpVisitor : public virtual DeclarationVisitor {
public:
bool Pre(const parser::OpenMPBlockConstruct &) {
PushScope(Scope::Kind::Block, nullptr);
return true;
}
void Post(const parser::OpenMPBlockConstruct &) { PopScope(); }
bool Pre(const parser::OpenMPLoopConstruct &) {
PushScope(Scope::Kind::Block, nullptr);
return true;
}
void Post(const parser::OpenMPLoopConstruct &) { PopScope(); }
bool Pre(const parser::OpenMPThreadprivate &x) {
PushScope(Scope::Kind::Block, nullptr);
const auto &list{std::get<parser::OmpObjectList>(x.t)};
ResolveOmpObjectList(list, Symbol::Flag::OmpThreadprivate);
PopScope();
return false;
}
bool Pre(const parser::OmpClause::Shared &x) {
ResolveOmpObjectList(x.v, Symbol::Flag::OmpShared);
return false;
}
bool Pre(const parser::OmpClause::Private &x) {
ResolveOmpObjectList(x.v, Symbol::Flag::OmpPrivate);
return false;
}
bool Pre(const parser::OmpClause::Firstprivate &x) {
ResolveOmpObjectList(x.v, Symbol::Flag::OmpFirstPrivate);
return false;
}
bool Pre(const parser::OmpClause::Lastprivate &x) {
ResolveOmpObjectList(x.v, Symbol::Flag::OmpLastPrivate);
return false;
}
protected:
// TODO: resolve variables referenced in the OpenMP region
void ResolveOmpObjectList(const parser::OmpObjectList &, Symbol::Flag);
void ResolveOmpObject(const parser::OmpObject &, Symbol::Flag);
Symbol *ResolveOmp(const parser::Name &, Symbol::Flag);
Symbol *ResolveOmp(Symbol &, Symbol::Flag);
Symbol *ResolveOmpCommonBlockName(const parser::Name *, Symbol::Flag);
Symbol *DeclarePrivateAccessEntity(const parser::Name &, Symbol::Flag);
Symbol *DeclarePrivateAccessEntity(Symbol &, Symbol::Flag);
Symbol *DeclareOrMarkOtherAccessEntity(const parser::Name &, Symbol::Flag);
Symbol *DeclareOrMarkOtherAccessEntity(Symbol &, Symbol::Flag);
};
Symbol *OmpVisitor::ResolveOmpCommonBlockName(
const parser::Name *name, Symbol::Flag ompFlag) {
if (auto *prev{name ? currScope().parent().FindCommonBlock(name->source)
: nullptr}) {
auto *created{FindInScope(currScope(), name->source)};
if (!created) {
auto &symbol{MakeSymbol(*name, HostAssocDetails{*prev})};
symbol.set(ompFlag);
name->symbol = &symbol;
} else {
name->symbol = created;
}
return prev;
} else {
return nullptr;
}
}
void OmpVisitor::ResolveOmpObjectList(
const parser::OmpObjectList &ompObjectList, Symbol::Flag ompFlag) {
for (const auto &ompObject : ompObjectList.v) {
ResolveOmpObject(ompObject, ompFlag);
}
}
void OmpVisitor::ResolveOmpObject(
const parser::OmpObject &ompObject, Symbol::Flag ompFlag) {
const auto &kind{std::get<parser::OmpObject::Kind>(ompObject.t)};
const auto &designator{std::get<parser::Designator>(ompObject.t)};
const auto *name{GetDesignatorNameIf(designator)};
if (kind == parser::OmpObject::Kind::Object) {
if (name) {
ResolveOmp(*name, ompFlag);
} else if (const auto *designatorName{ResolveDesignator(designator)};
designatorName->symbol) {
if (const auto *details{
designatorName->symbol->detailsIf<ObjectEntityDetails>()}) {
if (details->IsArray()) {
// TODO: check Array Sections
} else if (designatorName->symbol->owner().IsDerivedType()) {
// TODO: check Structure Component
} else {
Say(designatorName->source,
"Fortran Substrings are not allowed on OpenMP "
"directives or clauses"_err_en_US);
}
}
}
} else { // common block
if (auto *symbol{ResolveOmpCommonBlockName(name, ompFlag)}) {
// 2.15.3 When a named common block appears in a list, it has the same
// meaning as if every explicit member of the common block appeared in
// the list
for (Symbol *object : symbol->get<CommonBlockDetails>().objects()) {
ResolveOmp(*object, ompFlag);
}
} else {
Say(designator.source, // 2.15.3
"COMMON block must be declared in the same scoping unit "
"in which the directive or clause appears"_err_en_US);
}
}
}
Symbol *OmpVisitor::ResolveOmp(const parser::Name &name, Symbol::Flag ompFlag) {
if (ompFlagsRequireNewSymbol.test(ompFlag)) {
return DeclarePrivateAccessEntity(name, ompFlag);
} else {
return DeclareOrMarkOtherAccessEntity(name, ompFlag);
}
}
Symbol *OmpVisitor::ResolveOmp(Symbol &symbol, Symbol::Flag ompFlag) {
if (ompFlagsRequireNewSymbol.test(ompFlag)) {
return DeclarePrivateAccessEntity(symbol, ompFlag);
} else {
return DeclareOrMarkOtherAccessEntity(symbol, ompFlag);
}
}
Symbol *OmpVisitor::DeclarePrivateAccessEntity(
const parser::Name &name, Symbol::Flag ompFlag) {
Symbol &prev{FindOrDeclareEnclosingEntity(name)};
if (prev.owner() != currScope()) {
auto &symbol{MakeSymbol(name, HostAssocDetails{prev})};
symbol.set(ompFlag);
name.symbol = &symbol; // override resolution to parent
return &symbol;
} else {
return &prev;
}
}
Symbol *OmpVisitor::DeclarePrivateAccessEntity(
Symbol &object, Symbol::Flag ompFlag) {
if (object.owner() != currScope() &&
!FindInScope(currScope(), object.name())) {
auto &symbol{MakeSymbol(object.name(), Attrs{}, HostAssocDetails{object})};
symbol.set(ompFlag);
return &symbol;
} else {
return &object;
}
}
Symbol *OmpVisitor::DeclareOrMarkOtherAccessEntity(
const parser::Name &name, Symbol::Flag ompFlag) {
Symbol &prev{FindOrDeclareEnclosingEntity(name)};
name.symbol = &prev;
if (ompFlagsRequireMark.test(ompFlag)) {
prev.set(ompFlag);
}
return &prev;
}
Symbol *OmpVisitor::DeclareOrMarkOtherAccessEntity(
Symbol &object, Symbol::Flag ompFlag) {
if (ompFlagsRequireMark.test(ompFlag)) {
object.set(ompFlag);
}
return &object;
}
// Walk the parse tree and resolve names to symbols.
class ResolveNamesVisitor : public virtual ScopeHandler,
public ModuleVisitor,
public SubprogramVisitor,
public ConstructVisitor {
public ConstructVisitor,
public OmpVisitor {
public:
using ArraySpecVisitor::Post;
using ConstructVisitor::Post;
@ -1054,6 +1244,8 @@ public:
using InterfaceVisitor::Pre;
using ModuleVisitor::Post;
using ModuleVisitor::Pre;
using OmpVisitor::Post;
using OmpVisitor::Pre;
using ScopeHandler::Post;
using ScopeHandler::Pre;
using SubprogramVisitor::Post;

View File

@ -450,8 +450,15 @@ public:
CrayPointer, CrayPointee,
LocalityLocal, // named in LOCAL locality-spec
LocalityLocalInit, // named in LOCAL_INIT locality-spec
LocalityShared // named in SHARED locality-spec
);
LocalityShared, // named in SHARED locality-spec
// OpenMP data-sharing attribute
OmpShared, OmpPrivate, OmpLinear, OmpFirstPrivate, OmpLastPrivate,
// OpenMP data-mapping attribute
OmpMapTo, OmpMapFrom, OmpMapAlloc, OmpMapRelease, OmpMapDelete,
// OpenMP miscellaneous flags
OmpReduction, OmpDeclareSimd, OmpDeclareTarget, OmpThreadprivate,
OmpDeclareReduction, OmpFlushed, OmpCriticalLock, OmpIfSpecified);
using Flags = common::EnumSet<Flag, Flag_enumSize>;
const Scope &owner() const { return *owner_; }

View File

@ -41,6 +41,16 @@ public:
template<typename T> void Post(const parser::Statement<T> &) {
currStmt_ = std::nullopt;
}
bool Pre(const parser::OmpClause &clause) {
currStmt_ = clause.source;
return true;
}
void Post(const parser::OmpClause &) { currStmt_ = std::nullopt; }
bool Pre(const parser::OpenMPThreadprivate &dir) {
currStmt_ = dir.source;
return true;
}
void Post(const parser::OpenMPThreadprivate &) { currStmt_ = std::nullopt; }
void Post(const parser::Name &name);
private:

View File

@ -154,6 +154,13 @@ set(ERROR_TESTS
expr-errors01.f90
expr-errors02.f90
null01.f90
omp-resolve01.f90
omp-resolve02.f90
omp-symbol01.f90
omp-symbol02.f90
omp-symbol03.f90
omp-symbol04.f90
omp-symbol05.f90
omp-clause-validity01.f90
omp-loop-association.f90
# omp-nested01.f90

View File

@ -0,0 +1,29 @@
! Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved.
!
! Licensed under the Apache License, Version 2.0 (the "License");
! you may not use this file except in compliance with the License.
! You may obtain a copy of the License at
!
! http://www.apache.org/licenses/LICENSE-2.0
!
! Unless required by applicable law or agreed to in writing, software
! distributed under the License is distributed on an "AS IS" BASIS,
! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
! See the License for the specific language governing permissions and
! limitations under the License.
!OPTIONS: -fopenmp
! 2.4 An array section designates a subset of the elements in an array. Although
! Substring shares similar syntax but cannot be treated as valid array section.
character*8 c, b
character a
b = "HIFROMPGI"
c = b(2:7)
!ERROR: Fortran Substrings are not allowed on OpenMP directives or clauses
!$omp parallel private(c(1:3))
a = c(1:1)
!$omp end parallel
end

View File

@ -0,0 +1,31 @@
! Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved.
!
! Licensed under the Apache License, Version 2.0 (the "License");
! you may not use this file except in compliance with the License.
! You may obtain a copy of the License at
!
! http://www.apache.org/licenses/LICENSE-2.0
!
! Unless required by applicable law or agreed to in writing, software
! distributed under the License is distributed on an "AS IS" BASIS,
! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
! See the License for the specific language governing permissions and
! limitations under the License.
!OPTIONS: -fopenmp
! Test the effect to name resolution from illegal clause
!a = 1.0
b = 2
!$omp parallel private(a) shared(b)
a = 3.
b = 4
!ERROR: LASTPRIVATE clause is not allowed on the PARALLEL directive
!$omp parallel private(a) shared(b) lastprivate(a)
a = 5.
b = 6
!$omp end parallel
!$omp end parallel
print *,a, b
end

View File

@ -0,0 +1,80 @@
! Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved.
!
! Licensed under the Apache License, Version 2.0 (the "License");
! you may not use this file except in compliance with the License.
! You may obtain a copy of the License at
!
! http://www.apache.org/licenses/LICENSE-2.0
!
! Unless required by applicable law or agreed to in writing, software
! distributed under the License is distributed on an "AS IS" BASIS,
! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
! See the License for the specific language governing permissions and
! limitations under the License.
!OPTIONS: -fopenmp
! Test clauses that accept list.
! 2.1 Directive Format
! A list consists of a comma-separated collection of one or more list items.
! A list item is a variable, array section or common block name (enclosed in
! slashes).
!DEF: /md Module
module md
!DEF: /md/myty PUBLIC DerivedType
type :: myty
!DEF: /md/myty/a ObjectEntity REAL(4)
real :: a
!DEF: /md/myty/b ObjectEntity INTEGER(4)
integer :: b
end type myty
end module md
!DEF: /mm MainProgram
program mm
!REF: /md
use :: md
!DEF: /mm/c CommonBlockDetails
!DEF: /mm/x ObjectEntity REAL(4)
!DEF: /mm/y ObjectEntity REAL(4)
common /c/x, y
!REF: /mm/x
!REF: /mm/y
real x, y
!DEF: /mm/myty Use
!DEF: /mm/t ObjectEntity TYPE(myty)
type(myty) :: t
!DEF: /mm/b ObjectEntity INTEGER(4)
integer b(10)
!REF: /mm/t
!REF: /md/myty/a
t%a = 3.14
!REF: /mm/t
!REF: /md/myty/b
t%b = 1
!REF: /mm/b
b = 2
!DEF: /mm/a (Implicit) ObjectEntity REAL(4)
a = 1.0
!DEF: /mm/c (Implicit) ObjectEntity REAL(4)
c = 2.0
!$omp parallel do private(a,t,/c/)
!DEF: /mm/i (Implicit) ObjectEntity INTEGER(4)
do i=1,10
!DEF: /mm/Block1/a (OmpPrivate) HostAssoc REAL(4)
!REF: /mm/b
!REF: /mm/i
a = a+b(i)
!DEF: /mm/Block1/t (OmpPrivate) HostAssoc TYPE(myty)
!REF: /md/myty/a
!REF: /mm/i
t%a = i
!DEF: /mm/Block1/y (OmpPrivate) HostAssoc REAL(4)
y = 0.
!DEF: /mm/Block1/x (OmpPrivate) HostAssoc REAL(4)
!REF: /mm/Block1/a
!REF: /mm/i
!REF: /mm/Block1/y
x = a+i+y
end do
end program

View File

@ -0,0 +1,39 @@
! Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved.
!
! Licensed under the Apache License, Version 2.0 (the "License");
! you may not use this file except in compliance with the License.
! You may obtain a copy of the License at
!
! http://www.apache.org/licenses/LICENSE-2.0
!
! Unless required by applicable law or agreed to in writing, software
! distributed under the License is distributed on an "AS IS" BASIS,
! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
! See the License for the specific language governing permissions and
! limitations under the License.
!OPTIONS: -fopenmp
! 1.4.1 Structure of the OpenMP Memory Model
! Test implicit declaration in the OpenMP directive enclosing scope
! through clause; also test to avoid creating multiple symbols for
! the same variable
!DEF: /MainProgram1/b (Implicit) ObjectEntity REAL(4)
b = 2
!DEF: /MainProgram1/c (Implicit) ObjectEntity REAL(4)
c = 0
!$omp parallel private(a,b) shared(c,c,d,d)
!DEF: /MainProgram1/Block1/a (OmpPrivate) HostAssoc REAL(4)
a = 3.
!DEF: /MainProgram1/Block1/b (OmpPrivate) HostAssoc REAL(4)
b = 4
!REF: /MainProgram1/c
c = 5
!DEF: /MainProgram1/d (Implicit) ObjectEntity REAL(4)
d = 6
!$omp end parallel
!DEF: /MainProgram1/a (Implicit) ObjectEntity REAL(4)
print *, a
end program

View File

@ -0,0 +1,38 @@
! Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved.
!
! Licensed under the Apache License, Version 2.0 (the "License");
! you may not use this file except in compliance with the License.
! You may obtain a copy of the License at
!
! http://www.apache.org/licenses/LICENSE-2.0
!
! Unless required by applicable law or agreed to in writing, software
! distributed under the License is distributed on an "AS IS" BASIS,
! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
! See the License for the specific language governing permissions and
! limitations under the License.
!OPTIONS: -fopenmp
! 1.4.1 Structure of the OpenMP Memory Model
! In the inner OpenMP region, SHARED `a` refers to the `a` in the outer OpenMP
! region; PRIVATE `b` refers to the new `b` in the same OpenMP region
!DEF: /MainProgram1/b (Implicit) ObjectEntity REAL(4)
b = 2
!$omp parallel private(a) shared(b)
!DEF: /MainProgram1/Block1/a (OmpPrivate) HostAssoc REAL(4)
a = 3.
!REF: /MainProgram1/b
b = 4
!$omp parallel private(b) shared(a)
!REF: /MainProgram1/Block1/a
a = 5.
!DEF: /MainProgram1/Block1/Block1/b (OmpPrivate) HostAssoc REAL(4)
b = 6
!$omp end parallel
!$omp end parallel
!DEF: /MainProgram1/a (Implicit) ObjectEntity REAL(4)
!REF: /MainProgram1/b
print *, a, b
end program

View File

@ -0,0 +1,37 @@
! Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved.
!
! Licensed under the Apache License, Version 2.0 (the "License");
! you may not use this file except in compliance with the License.
! You may obtain a copy of the License at
!
! http://www.apache.org/licenses/LICENSE-2.0
!
! Unless required by applicable law or agreed to in writing, software
! distributed under the License is distributed on an "AS IS" BASIS,
! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
! See the License for the specific language governing permissions and
! limitations under the License.
!OPTIONS: -fopenmp
! 2.15.3 Data-Sharing Attribute Clauses
! Both PARALLEL and DO (worksharing) directives need to create new scope,
! so PRIVATE `a` will have new symbol in each region
!DEF: /MainProgram1/a ObjectEntity REAL(8)
real*8 a
!REF: /MainProgram1/a
a = 3.14
!$omp parallel private(a)
!DEF: /MainProgram1/Block1/a (OmpPrivate) HostAssoc REAL(8)
a = 2.
!$omp do private(a)
!DEF: /MainProgram1/i (Implicit) ObjectEntity INTEGER(4)
do i=1,10
!DEF: /MainProgram1/Block1/Block1/a (OmpPrivate) HostAssoc REAL(8)
a = 1.
end do
!$omp end parallel
!REF: /MainProgram1/a
print *, a
end program

View File

@ -0,0 +1,54 @@
! Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved.
!
! Licensed under the Apache License, Version 2.0 (the "License");
! you may not use this file except in compliance with the License.
! You may obtain a copy of the License at
!
! http://www.apache.org/licenses/LICENSE-2.0
!
! Unless required by applicable law or agreed to in writing, software
! distributed under the License is distributed on an "AS IS" BASIS,
! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
! See the License for the specific language governing permissions and
! limitations under the License.
!OPTIONS: -fopenmp
! 2.15.2 threadprivate Directive
! The threadprivate directive specifies that variables are replicated,
! with each thread having its own copy. When threadprivate variables are
! referenced in the OpenMP region, we know they are already private to
! their threads, so no new symbol needs to be created.
!DEF: /mm Module
module mm
!$omp threadprivate (i)
contains
!DEF: /mm/foo PUBLIC (Subroutine) Subprogram
subroutine foo
!DEF: /mm/foo/a ObjectEntity INTEGER(4)
integer :: a = 3
!$omp parallel
!REF: /mm/foo/a
a = 1
!DEF: /mm/i PUBLIC (Implicit, OmpThreadprivate) ObjectEntity INTEGER(4)
!REF: /mm/foo/a
i = a
!$omp end parallel
!REF: /mm/foo/a
print *, a
block
!DEF: /mm/foo/Block2/i ObjectEntity REAL(4)
real i
!REF: /mm/foo/Block2/i
i = 3.14
end block
end subroutine foo
end module mm
!DEF: /tt MainProgram
program tt
!REF: /mm
use :: mm
!DEF: /tt/foo (Subroutine) Use
call foo
end program tt