[flang][OpenMP] Store DECLARE_TARGET information in WithOmpDeclarative (#201103)

This will be used to emit DECLARE_TARGET directives into module files.

When a symbol apperars in DECLARE_TARGET, the OmpDeclareTarget flag will
be set on it (this includes procedures containing a DECLARE_TARGET
without arguments or clauses). The set of accompanying clauses will be
stored in the associated details, in the WithOmpDeclarative mixin. The
mixin was added to ObjectEntityDetails, ProcEntityDetails, and
CommonBlockDetails.

The design goal was to be able to reconstruct the appropriate DECLARE_
TARGET directive for individual symbols for the purpose of emitting it
in a module file. Simply storing and then unparsing the AST node may
include symbols that should not be emitted.

Additionally, refactor the WithOmpDeclarative printing code for reuse in
symbol dumping for debugging, and for printing clause sets.
This commit is contained in:
Krzysztof Parzyszek 2026-06-03 09:40:42 -05:00 committed by GitHub
parent 3a9c6cdb5d
commit 270a6a8f67
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 280 additions and 67 deletions

View file

@ -137,8 +137,7 @@ void OpenMPCounterVisitor::Post(
}
void OpenMPCounterVisitor::Post(
const OmpDeviceTypeClause::DeviceTypeDescription &c) {
clauseDetails +=
"type=" + std::string{OmpDeviceTypeClause::EnumToString(c)} + ";";
clauseDetails += "type=" + std::string{common::EnumToString(c)} + ";";
}
void OpenMPCounterVisitor::Post(
const OmpDefaultmapClause::ImplicitBehavior &c) {

View file

@ -66,6 +66,7 @@ public:
NODE_ENUM(common, CUDASubprogramAttrs)
NODE_ENUM(common, ImportKind)
NODE_ENUM(common, OmpDependenceKind)
NODE_ENUM(common, OmpDeviceType)
NODE_ENUM(common, OmpMemoryOrderType)
NODE_ENUM(common, OpenACCDeviceType)
NODE(format, ControlEditDesc)
@ -605,7 +606,6 @@ public:
NODE_ENUM(OmpDeviceModifier, Value)
NODE(parser, OmpDeviceSafesyncClause)
NODE(parser, OmpDeviceTypeClause)
NODE_ENUM(OmpDeviceTypeClause, DeviceTypeDescription)
NODE(parser, OmpDimsModifier)
NODE(parser, OmpDirectiveName)
NODE(parser, OmpDirectiveSpecification)

View file

@ -4591,7 +4591,7 @@ struct OmpDeviceSafesyncClause {
// device-type-clause ->
// DEVICE_TYPE(ANY | HOST | NOHOST) // since 5.0
struct OmpDeviceTypeClause {
ENUM_CLASS(DeviceTypeDescription, Any, Host, Nohost)
using DeviceTypeDescription = common::OmpDeviceType;
WRAPPER_CLASS_BOILERPLATE(OmpDeviceTypeClause, DeviceTypeDescription);
};

View file

@ -66,6 +66,18 @@ public:
ompAtomicDefaultMemOrder_ = flags;
}
const OmpClauseSet &ompDeclTarget() const { return ompDeclTarget_; }
void set_ompDeclTarget(OmpClauseSet clauses) { ompDeclTarget_ = clauses; }
const std::optional<common::OmpDeviceType> &ompDeviceType() const {
return ompDeviceType_;
}
void set_ompDeclTarget(common::OmpDeviceType device) {
ompDeviceType_ = device;
}
void printClauseSet(llvm::raw_ostream &os, const OmpClauseSet &clauses,
parser::CharBlock name = parser::CharBlock{}) const;
friend llvm::raw_ostream &operator<<(
llvm::raw_ostream &, const WithOmpDeclarative &);
@ -81,6 +93,12 @@ private:
// The argument to ATOMIC_DEFAULT_MEM_ORDER. Only needed when the ADMO
// clause is present in the ompRequires_ set.
std::optional<common::OmpMemoryOrderType> ompAtomicDefaultMemOrder_;
// The set of clauses on DECLARE_TARGET directive that apply to this
// symbol.
OmpClauseSet ompDeclTarget_;
// The argument to DEVICE_TYPE clause. Only needed when the clause is
// present in the ompDeclTarget_ set.
std::optional<common::OmpDeviceType> ompDeviceType_;
};
// A module or submodule.
@ -379,7 +397,7 @@ private:
llvm::raw_ostream &operator<<(llvm::raw_ostream &, const AssocEntityDetails &);
// An entity known to be an object.
class ObjectEntityDetails : public EntityDetails {
class ObjectEntityDetails : public EntityDetails, public WithOmpDeclarative {
public:
explicit ObjectEntityDetails(EntityDetails &&);
ObjectEntityDetails(const ObjectEntityDetails &) = default;
@ -450,7 +468,9 @@ private:
// A procedure pointer (other than one defined with POINTER and an
// INTERFACE block), a dummy procedure (without an INTERFACE but with
// EXTERNAL or use in a procedure reference), or external procedure.
class ProcEntityDetails : public EntityDetails, public WithPassArg {
class ProcEntityDetails : public EntityDetails,
public WithPassArg,
public WithOmpDeclarative {
public:
ProcEntityDetails() = default;
explicit ProcEntityDetails(EntityDetails &&);
@ -587,7 +607,7 @@ private:
SymbolVector objects_;
};
class CommonBlockDetails : public WithBindName {
class CommonBlockDetails : public WithBindName, public WithOmpDeclarative {
public:
explicit CommonBlockDetails(SourceName location)
: sourceLocation_{location} {}

View file

@ -78,6 +78,9 @@ ENUM_CLASS(OmpDependenceKind, In, Out, Inout, Inoutset, Mutexinoutset, Depobj)
// OpenMP memory-order types
ENUM_CLASS(OmpMemoryOrderType, Acq_Rel, Acquire, Relaxed, Release, Seq_Cst)
// OpenMP device-type
ENUM_CLASS(OmpDeviceType, Any, Host, Nohost)
// Fortran names may have up to 63 characters (See Fortran 2018 C601).
static constexpr int maxNameLen{63};

View file

@ -2846,6 +2846,7 @@ public:
WALK_NESTED_ENUM(common, CUDADataAttr) // CUDA
WALK_NESTED_ENUM(common, CUDASubprogramAttrs) // CUDA
WALK_NESTED_ENUM(common, OmpDependenceKind)
WALK_NESTED_ENUM(common, OmpDeviceType)
WALK_NESTED_ENUM(common, OmpMemoryOrderType)
WALK_NESTED_ENUM(IntentSpec, Intent) // R826
WALK_NESTED_ENUM(ImplicitStmt, ImplicitNoneNameSpec) // R866
@ -2873,8 +2874,6 @@ public:
WALK_NESTED_ENUM(OmpThreadsetClause, ThreadsetPolicy) // OMP threadset
WALK_NESTED_ENUM(OmpAccessGroup, Value)
WALK_NESTED_ENUM(OmpDeviceModifier, Value) // OMP device modifier
WALK_NESTED_ENUM(
OmpDeviceTypeClause, DeviceTypeDescription) // OMP device_type
WALK_NESTED_ENUM(OmpReductionModifier, Value) // OMP reduction-modifier
WALK_NESTED_ENUM(OmpExpectation, Value) // OMP motion-expectation
WALK_NESTED_ENUM(OmpFallbackModifier, Value) // OMP fallback-modifier

View file

@ -364,39 +364,51 @@ void ModFileWriter::PrepareRenamings(const Scope &scope) {
}
}
static const WithOmpDeclarative *GetOmpDeclarative(const Symbol &symbol) {
return common::visit(
[&](auto &&details) -> const WithOmpDeclarative * {
using TypeD = llvm::remove_cvref_t<decltype(details)>;
if constexpr (std::is_base_of_v<WithOmpDeclarative, TypeD>) {
return &static_cast<const WithOmpDeclarative &>(details);
} else {
return nullptr;
}
},
symbol.details());
}
static void PutOpenMPRequirements(
llvm::raw_ostream &os, const Symbol &symbol, SemanticsContext &semaCtx) {
using OmpClauseSet = WithOmpDeclarative::OmpClauseSet;
using OmpMemoryOrderType = common::OmpMemoryOrderType;
unsigned version{semaCtx.langOptions().OpenMPVersion};
const auto [reqs, order]{common::visit(
[&](auto &&details)
-> std::pair<const OmpClauseSet *, const OmpMemoryOrderType *> {
if constexpr (std::is_convertible_v<decltype(details),
const WithOmpDeclarative &>) {
if (const auto &memOrder{details.ompAtomicDefaultMemOrder()}) {
return {&details.ompRequires(), &*memOrder};
}
return {&details.ompRequires(), nullptr};
} else {
return {nullptr, nullptr};
}
},
symbol.details())};
if (const auto *decls{GetOmpDeclarative(symbol)}) {
if (const OmpClauseSet &reqs{decls->ompRequires()}; reqs.count()) {
os << "!$omp "
<< parser::ToLowerCaseLetters(llvm::omp::getOpenMPDirectiveName(
llvm::omp::Directive::OMPD_requires, version));
decls->printClauseSet(os, reqs);
os << "\n";
}
}
}
if (reqs->count()) {
os << "!$omp requires";
reqs->IterateOverMembers([&, order = order](llvm::omp::Clause f) {
os << ' '
<< parser::ToLowerCaseLetters(
llvm::omp::getOpenMPClauseName(f, version));
if (f == llvm::omp::Clause::OMPC_atomic_default_mem_order) {
os << '(' << parser::ToLowerCaseLetters(EnumToString(DEREF(order)))
<< ')';
static void PutOpenMPDeclarativeDirectives(llvm::raw_ostream &os,
const SymbolVector &symbols, SemanticsContext &semaCtx) {
using OmpClauseSet = WithOmpDeclarative::OmpClauseSet;
unsigned version{semaCtx.langOptions().OpenMPVersion};
for (const Symbol &symbol : symbols) {
if (const auto *decls{GetOmpDeclarative(symbol)}) {
if (const OmpClauseSet &dtgt{decls->ompDeclTarget()}; dtgt.count()) {
os << "!$omp "
<< parser::ToLowerCaseLetters(llvm::omp::getOpenMPDirectiveName(
llvm::omp::Directive::OMPD_declare_target, version))
<< " ";
decls->printClauseSet(os, dtgt, symbol.name());
os << "\n";
}
});
os << "\n";
}
}
}
@ -438,6 +450,8 @@ void ModFileWriter::PutSymbols(
PutUse(symbol);
}
PutOpenMPRequirements(decls_, DEREF(scope.symbol()), context_);
PutOpenMPDeclarativeDirectives(decls_, sorted, context_);
for (const auto &set : scope.equivalenceSets()) {
if (!set.empty() &&
!set.front().symbol.test(Symbol::Flag::CompilerCreated)) {

View file

@ -2191,9 +2191,30 @@ bool OmpAttributeVisitor::Pre(const parser::OpenMPCriticalConstruct &x) {
bool OmpAttributeVisitor::Pre(const parser::OmpDeclareTargetDirective &x) {
PushContext(x.source, llvm::omp::Directive::OMPD_declare_target);
unsigned version{context_.langOptions().OpenMPVersion};
using OmpClauseSet = WithOmpDeclarative::OmpClauseSet;
std::map<const Symbol *, WithOmpDeclarative> details;
std::optional<common::OmpDeviceType> device;
if (auto *devClause{
parser::omp::FindClause(x.v, llvm::omp::Clause::OMPC_device_type)}) {
device = parser::UnwrapRef<common::OmpDeviceType>(*devClause);
}
auto addClause{[&](const parser::OmpObject &object,
llvm::omp::Clause clauseId) {
if (const Symbol *sym{omp::GetObjectSymbol(object)}) {
auto &clauseSet{const_cast<OmpClauseSet &>(details[sym].ompDeclTarget())};
clauseSet.set(clauseId);
}
}};
for (const parser::OmpArgument &arg : x.v.Arguments().v) {
if (auto *object{parser::omp::GetArgumentObject(arg)}) {
ResolveOmpObject(*object, Symbol::Flag::OmpDeclareTarget);
// Always record this as "enter", even with older OpenMP versions
// (as opposed to "to").
addClause(*object, llvm::omp::Clause::OMPC_enter);
}
}
@ -2201,9 +2222,53 @@ bool OmpAttributeVisitor::Pre(const parser::OmpDeclareTargetDirective &x) {
if (auto *objects{parser::omp::GetOmpObjectList(clause)}) {
for (const parser::OmpObject &object : objects->v) {
ResolveOmpObject(object, Symbol::Flag::OmpDeclareTarget);
addClause(object, clause.Id());
}
}
}
if (x.v.Arguments().v.empty() && x.v.Clauses().v.empty()) {
// "!$omp declare_target" alone applies to the associated procedure.
const Scope &scope{omp::GetScopingUnit(context_.FindScope(x.source))};
if (auto *proc{const_cast<Symbol *>(scope.symbol())}) {
proc->flags().set(Symbol::Flag::OmpDeclareTarget);
auto &clauseSet{
const_cast<OmpClauseSet &>(details[proc].ompDeclTarget())};
clauseSet.set(llvm::omp::Clause::OMPC_enter);
}
}
for (auto &pair : details) {
const Symbol *sym{&pair.first->GetUltimate()};
const WithOmpDeclarative &decl{pair.second};
common::visit(
[&](auto &d) {
using TypeD = llvm::remove_cvref_t<decltype(d)>;
if constexpr (std::is_base_of_v<WithOmpDeclarative, TypeD>) {
d.set_version(version);
auto &clauseSet{const_cast<OmpClauseSet &>(d.ompDeclTarget())};
clauseSet |= decl.ompDeclTarget();
if (device) {
clauseSet.set(llvm::omp::Clause::OMPC_device_type);
auto &deviceType{
const_cast<decltype(device) &>(d.ompDeviceType())};
deviceType = device;
}
} else if constexpr (std::is_same_v<GenericDetails, TypeD> ||
std::is_same_v<TypeParamDetails, TypeD> ||
std::is_same_v<MiscDetails, TypeD>) { // e.g. KindParamInquiry
// These cannot be specified on DECLARE_TARGET. The symbol will
// receive the OmpDeclareTarget flag (for a diagnostic message),
// but no details will be modified.
} else {
// Catch any unexpected cases.
assert((std::is_base_of_v<WithOmpDeclarative, TypeD>) &&
"Unexpected details type");
}
},
const_cast<Symbol *>(sym)->details());
}
return true;
}

View file

@ -70,33 +70,50 @@ static void DumpList(llvm::raw_ostream &os, const char *label, const T &list) {
}
}
void WithOmpDeclarative::printClauseSet(llvm::raw_ostream &os,
const OmpClauseSet &clauses, parser::CharBlock name) const {
auto toLower = parser::ToLowerCaseLetters;
size_t idx{0}, size{clauses.count()};
clauses.IterateOverMembers([&](llvm::omp::Clause c) {
os << toLower(llvm::omp::getOpenMPClauseName(c, version_));
switch (c) {
case llvm::omp::Clause::OMPC_atomic_default_mem_order:
os << '(' << toLower(EnumToString(*ompAtomicDefaultMemOrder())) << ')';
break;
case llvm::omp::Clause::OMPC_device_type:
os << "(" << toLower(EnumToString(*ompDeviceType())) << ')';
break;
case llvm::omp::Clause::OMPC_enter:
case llvm::omp::Clause::OMPC_link:
if (!name.empty()) {
os << '(' << name.ToString() << ')';
}
break;
case llvm::omp::Clause::OMPC_indirect:
os << "(true)";
break;
default:
break;
}
if (++idx < size) {
os << ' ';
}
});
}
llvm::raw_ostream &operator<<(
llvm::raw_ostream &os, const WithOmpDeclarative &x) {
using OmpClauseSet = WithOmpDeclarative::OmpClauseSet;
auto toLower = parser::ToLowerCaseLetters;
auto printClauses = [&](const OmpClauseSet &cs) {
size_t idx{0}, size{cs.count()};
cs.IterateOverMembers([&](llvm::omp::Clause c) {
os << toLower(llvm::omp::getOpenMPClauseName(c, x.version_));
switch (c) {
case llvm::omp::Clause::OMPC_atomic_default_mem_order:
os << '(' << toLower(EnumToString(*x.ompAtomicDefaultMemOrder()))
<< ')';
break;
default:
break;
}
if (++idx < size) {
os << ',';
}
});
};
if (const OmpClauseSet &reqs{x.ompRequires()}; reqs.count()) {
os << " OmpRequirements:(";
printClauses(reqs);
x.printClauseSet(os, reqs);
os << ')';
}
if (const OmpClauseSet &dtgt{x.ompDeclTarget()}; dtgt.count()) {
os << " OmpDeclareTargetFlags:(";
x.printClauseSet(os, dtgt);
os << ')';
}
return os;
@ -541,6 +558,7 @@ llvm::raw_ostream &operator<<(
if (x.cudaDataAttr()) {
os << " cudaDataAttr: " << common::EnumToString(*x.cudaDataAttr());
}
os << static_cast<const WithOmpDeclarative &>(x);
return os;
}
@ -580,6 +598,7 @@ llvm::raw_ostream &operator<<(
if (x.isCUDAKernel()) {
os << " isCUDAKernel";
}
os << static_cast<const WithOmpDeclarative &>(x);
return os;
}
@ -675,6 +694,7 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const Details &details) {
for (const auto &object : x.objects()) {
os << ' ' << object->name();
}
os << static_cast<const WithOmpDeclarative &>(x);
},
[&](const TypeParamDetails &x) {
DumpOptional(os, "type", x.type());

View file

@ -7,7 +7,7 @@ subroutine openmp_declare_target
!PARSE-TREE: DeclarationConstruct -> SpecificationConstruct -> OpenMPDeclarativeConstruct -> OmpDeclareTargetDirective -> OmpDirectiveSpecification
!PARSE-TREE: | OmpDirectiveName -> llvm::omp::Directive = declare target
!PARSE-TREE: | OmpClauseList -> OmpClause -> DeviceType -> OmpDeviceTypeClause -> DeviceTypeDescription = Host
!PARSE-TREE: | OmpClauseList -> OmpClause -> DeviceType -> OmpDeviceTypeClause -> OmpDeviceType = Host
!PARSE-TREE: | OmpClause -> Enter -> OmpEnterClause
!PARSE-TREE: | | OmpObjectList -> OmpObject -> Designator -> DataRef -> Name = 'x'
!PARSE-TREE: | Flags = {}
@ -17,7 +17,7 @@ subroutine openmp_declare_target
!PARSE-TREE: DeclarationConstruct -> SpecificationConstruct -> OpenMPDeclarativeConstruct -> OmpDeclareTargetDirective -> OmpDirectiveSpecification
!PARSE-TREE: | OmpDirectiveName -> llvm::omp::Directive = declare target
!PARSE-TREE: | OmpClauseList -> OmpClause -> DeviceType -> OmpDeviceTypeClause -> DeviceTypeDescription = Nohost
!PARSE-TREE: | OmpClauseList -> OmpClause -> DeviceType -> OmpDeviceTypeClause -> OmpDeviceType = Nohost
!PARSE-TREE: | OmpClause -> Enter -> OmpEnterClause
!PARSE-TREE: | | OmpObjectList -> OmpObject -> Designator -> DataRef -> Name = 'x'
!PARSE-TREE: | Flags = {}
@ -27,7 +27,7 @@ subroutine openmp_declare_target
!PARSE-TREE: DeclarationConstruct -> SpecificationConstruct -> OpenMPDeclarativeConstruct -> OmpDeclareTargetDirective -> OmpDirectiveSpecification
!PARSE-TREE: | OmpDirectiveName -> llvm::omp::Directive = declare target
!PARSE-TREE: | OmpClauseList -> OmpClause -> DeviceType -> OmpDeviceTypeClause -> DeviceTypeDescription = Any
!PARSE-TREE: | OmpClauseList -> OmpClause -> DeviceType -> OmpDeviceTypeClause -> OmpDeviceType = Any
!PARSE-TREE: | OmpClause -> Enter -> OmpEnterClause
!PARSE-TREE: | | OmpObjectList -> OmpObject -> Designator -> DataRef -> Name = 'x'
!PARSE-TREE: | Flags = {}
@ -37,7 +37,7 @@ subroutine openmp_declare_target
!PARSE-TREE: DeclarationConstruct -> SpecificationConstruct -> OpenMPDeclarativeConstruct -> OmpDeclareTargetDirective -> OmpDirectiveSpecification
!PARSE-TREE: | OmpDirectiveName -> llvm::omp::Directive = declare target
!PARSE-TREE: | OmpClauseList -> OmpClause -> DeviceType -> OmpDeviceTypeClause -> DeviceTypeDescription = Host
!PARSE-TREE: | OmpClauseList -> OmpClause -> DeviceType -> OmpDeviceTypeClause -> OmpDeviceType = Host
!PARSE-TREE: | OmpClause -> To -> OmpToClause
!PARSE-TREE: | | OmpObjectList -> OmpObject -> Designator -> DataRef -> Name = 'x'
!PARSE-TREE: | | bool = 'true'
@ -48,7 +48,7 @@ subroutine openmp_declare_target
!PARSE-TREE: DeclarationConstruct -> SpecificationConstruct -> OpenMPDeclarativeConstruct -> OmpDeclareTargetDirective -> OmpDirectiveSpecification
!PARSE-TREE: | OmpDirectiveName -> llvm::omp::Directive = declare target
!PARSE-TREE: | OmpClauseList -> OmpClause -> DeviceType -> OmpDeviceTypeClause -> DeviceTypeDescription = Nohost
!PARSE-TREE: | OmpClauseList -> OmpClause -> DeviceType -> OmpDeviceTypeClause -> OmpDeviceType = Nohost
!PARSE-TREE: | OmpClause -> To -> OmpToClause
!PARSE-TREE: | | OmpObjectList -> OmpObject -> Designator -> DataRef -> Name = 'x'
!PARSE-TREE: | | bool = 'true'
@ -59,7 +59,7 @@ subroutine openmp_declare_target
!PARSE-TREE: DeclarationConstruct -> SpecificationConstruct -> OpenMPDeclarativeConstruct -> OmpDeclareTargetDirective -> OmpDirectiveSpecification
!PARSE-TREE: | OmpDirectiveName -> llvm::omp::Directive = declare target
!PARSE-TREE: | OmpClauseList -> OmpClause -> DeviceType -> OmpDeviceTypeClause -> DeviceTypeDescription = Any
!PARSE-TREE: | OmpClauseList -> OmpClause -> DeviceType -> OmpDeviceTypeClause -> OmpDeviceType = Any
!PARSE-TREE: | OmpClause -> To -> OmpToClause
!PARSE-TREE: | | OmpObjectList -> OmpObject -> Designator -> DataRef -> Name = 'x'
!PARSE-TREE: | | bool = 'true'
@ -70,7 +70,7 @@ subroutine openmp_declare_target
!PARSE-TREE: DeclarationConstruct -> SpecificationConstruct -> OpenMPDeclarativeConstruct -> OmpDeclareTargetDirective -> OmpDirectiveSpecification
!PARSE-TREE: | OmpDirectiveName -> llvm::omp::Directive = declare target
!PARSE-TREE: | OmpClauseList -> OmpClause -> DeviceType -> OmpDeviceTypeClause -> DeviceTypeDescription = Host
!PARSE-TREE: | OmpClauseList -> OmpClause -> DeviceType -> OmpDeviceTypeClause -> OmpDeviceType = Host
!PARSE-TREE: | OmpClause -> Enter -> OmpEnterClause
!PARSE-TREE: | | OmpObjectList -> OmpObject -> Designator -> DataRef -> Name = 'y'
!PARSE-TREE: | OmpClause -> To -> OmpToClause
@ -83,7 +83,7 @@ subroutine openmp_declare_target
!PARSE-TREE: DeclarationConstruct -> SpecificationConstruct -> OpenMPDeclarativeConstruct -> OmpDeclareTargetDirective -> OmpDirectiveSpecification
!PARSE-TREE: | OmpDirectiveName -> llvm::omp::Directive = declare target
!PARSE-TREE: | OmpClauseList -> OmpClause -> DeviceType -> OmpDeviceTypeClause -> DeviceTypeDescription = Nohost
!PARSE-TREE: | OmpClauseList -> OmpClause -> DeviceType -> OmpDeviceTypeClause -> OmpDeviceType = Nohost
!PARSE-TREE: | OmpClause -> Enter -> OmpEnterClause
!PARSE-TREE: | | OmpObjectList -> OmpObject -> Designator -> DataRef -> Name = 'y'
!PARSE-TREE: | OmpClause -> To -> OmpToClause
@ -96,7 +96,7 @@ subroutine openmp_declare_target
!PARSE-TREE: DeclarationConstruct -> SpecificationConstruct -> OpenMPDeclarativeConstruct -> OmpDeclareTargetDirective -> OmpDirectiveSpecification
!PARSE-TREE: | OmpDirectiveName -> llvm::omp::Directive = declare target
!PARSE-TREE: | OmpClauseList -> OmpClause -> DeviceType -> OmpDeviceTypeClause -> DeviceTypeDescription = Any
!PARSE-TREE: | OmpClauseList -> OmpClause -> DeviceType -> OmpDeviceTypeClause -> OmpDeviceType = Any
!PARSE-TREE: | OmpClause -> Enter -> OmpEnterClause
!PARSE-TREE: | | OmpObjectList -> OmpObject -> Designator -> DataRef -> Name = 'y'
!PARSE-TREE: | OmpClause -> To -> OmpToClause

View file

@ -21,7 +21,7 @@ end module
!PARSE-TREE: | OmpDirectiveName -> llvm::omp::Directive = groupprivate
!PARSE-TREE: | OmpArgumentList -> OmpArgument -> OmpLocator -> OmpObject -> Designator -> DataRef -> Name = 'x'
!PARSE-TREE: | OmpArgument -> OmpLocator -> OmpObject -> Designator -> DataRef -> Name = 'y'
!PARSE-TREE: | OmpClauseList -> OmpClause -> DeviceType -> OmpDeviceTypeClause -> DeviceTypeDescription = Nohost
!PARSE-TREE: | OmpClauseList -> OmpClause -> DeviceType -> OmpDeviceTypeClause -> OmpDeviceType = Nohost
!PARSE-TREE: | Flags = {}
!PARSE-TREE: DeclarationConstruct -> SpecificationConstruct -> OpenMPDeclarativeConstruct -> OmpGroupprivateDirective -> OmpDirectiveSpecification
!PARSE-TREE: | OmpDirectiveName -> llvm::omp::Directive = groupprivate

View file

@ -0,0 +1,43 @@
!RUN: %flang_fc1 -fdebug-dump-symbols -fopenmp -fopenmp-version=60 %s | FileCheck %s
module m
integer :: x
!$omp declare_target link(x) device_type(nohost)
real :: w(10), u(10)
common /named_block/ w, u
!$omp declare_target link(/named_block/)
interface
real function g(v)
real :: v(10)
!$omp declare_target
end
end interface
contains
subroutine f
!$omp declare_target(f)
end
subroutine h
integer, save :: a(10)
!$omp declare_target enter(h, a)
continue
end
end module
!CHECK: Module scope: m size=4 alignment=4 sourceRange=383 bytes
!CHECK: f, PUBLIC (Subroutine): Subprogram () OmpDeclareTargetFlags:(enter)
!CHECK: g, EXTERNAL, PUBLIC (Function, OmpDeclareTarget): Subprogram isInterface result:REAL(4) g (REAL(4) v) OmpDeclareTargetFlags:(enter)
!CHECK: h, PUBLIC (Subroutine): Subprogram () OmpDeclareTargetFlags:(enter)
!CHECK: u, PUBLIC (InCommonBlock, OmpDeclareTarget) size=40 offset=40: ObjectEntity type: REAL(4) shape: 1_8:10_8
!CHECK: w, PUBLIC (InCommonBlock, OmpDeclareTarget) size=40 offset=0: ObjectEntity type: REAL(4) shape: 1_8:10_8
!CHECK: x, PUBLIC (OmpDeclareTarget) size=4 offset=0: ObjectEntity type: INTEGER(4) OmpDeclareTargetFlags:(device_type(nohost) link)
!CHECK: named_block size=80 offset=0: CommonBlockDetails alignment=4: w u OmpDeclareTargetFlags:(link)
!CHECK: Subprogram scope: g size=44 alignment=4 sourceRange=57 bytes
!CHECK: g size=4 offset=0: ObjectEntity funcResult type: REAL(4)
!CHECK: v size=40 offset=4: ObjectEntity dummy type: REAL(4) shape: 1_8:10_8
!CHECK: Subprogram scope: f size=0 alignment=1 sourceRange=40 bytes
!CHECK: f (Subroutine, OmpDeclareTarget): HostAssoc => f, PUBLIC (Subroutine): Subprogram () OmpDeclareTargetFlags:(enter)
!CHECK: Subprogram scope: h size=40 alignment=4 sourceRange=81 bytes
!CHECK: a, SAVE (OmpDeclareTarget) size=40 offset=0: ObjectEntity type: INTEGER(4) shape: 1_8:10_8 OmpDeclareTargetFlags:(enter)
!CHECK: h (Subroutine, OmpDeclareTarget): HostAssoc => h, PUBLIC (Subroutine): Subprogram () OmpDeclareTargetFlags:(enter)

View file

@ -0,0 +1,50 @@
!RUN: %python %S/../test_modfile.py %s %flang_fc1 -fopenmp -fopenmp-version=60
module m
integer :: x
!$omp declare_target link(x) device_type(nohost)
real :: w(10), u(10)
common /named_block/ w, u
!$omp declare_target link(/named_block/)
interface
real function g(v)
real :: v(10)
!$omp declare_target
end
end interface
contains
subroutine f
!$omp declare_target(f)
end
subroutine h
integer, save :: a(10)
!$omp declare_target enter(h, a)
continue
end
end module
!Expect: m.mod
!module m
!integer(4)::x
!real(4)::w(1_8:10_8)
!real(4)::u(1_8:10_8)
!interface
!function g(v)
!real(4)::v(1_8:10_8)
!real(4)::g
!end
!end interface
!common/named_block/w,u
!!$omp declare_target device_type(nohost) link(x)
!!$omp declare_target enter(g)
!!$omp declare_target enter(f)
!!$omp declare_target enter(h)
!!$omp declare_target link(named_block)
!contains
!subroutine f()
!end
!subroutine h()
!end
!end

View file

@ -11,4 +11,4 @@ subroutine f01
end
end module
!CHECK: fred: Module OmpRequirements:(atomic_default_mem_order(relaxed),unified_address,unified_shared_memory)
!CHECK: fred: Module OmpRequirements:(atomic_default_mem_order(relaxed) unified_address unified_shared_memory)