diff --git a/flang/include/flang/Parser/dump-parse-tree.h b/flang/include/flang/Parser/dump-parse-tree.h index 8527f0c545177..466cec9003038 100644 --- a/flang/include/flang/Parser/dump-parse-tree.h +++ b/flang/include/flang/Parser/dump-parse-tree.h @@ -580,6 +580,7 @@ class ParseTreeDumper { NODE(OmpDependClause::TaskDep, Modifier) NODE(parser, OmpDependenceType) NODE_ENUM(OmpDependenceType, Value) + NODE(parser, OmpDepinfoModifier) NODE(parser, OmpDestroyClause) NODE(parser, OmpDetachClause) NODE(parser, OmpDeviceClause) diff --git a/flang/include/flang/Parser/parse-tree.h b/flang/include/flang/Parser/parse-tree.h index a31eb542a50d0..6ac195f9357a5 100644 --- a/flang/include/flang/Parser/parse-tree.h +++ b/flang/include/flang/Parser/parse-tree.h @@ -3970,6 +3970,18 @@ struct OmpDependenceType { WRAPPER_CLASS_BOILERPLATE(OmpDependenceType, Value); }; +// Ref: [6.0:180-181] +// +// depinfo-modifier -> // since 6.0 +// keyword (locator-list-item) +// keyword -> +// IN | INOUT | INOUTSET | MUTEXINOUTSET | OUT // since 6.0 +struct OmpDepinfoModifier { + using Value = common::OmpDependenceKind; + TUPLE_CLASS_BOILERPLATE(OmpDepinfoModifier); + std::tuple t; +}; + // Ref: [5.0:170-176], [5.1:197-205], [5.2:276-277] // // device-modifier -> @@ -4670,6 +4682,19 @@ struct OmpIfClause { std::tuple t; }; +// Ref: [5.1:217-220], [5.2:293-294], [6.0:180-181] +// +// init-clause -> +// INIT ([modifier... :] interop-var) // since 5.1 +// modifier -> +// prefer-type | interop-type | // since 5.1 +// depinfo-modifier // since 6.0 +struct OmpInitClause { + TUPLE_CLASS_BOILERPLATE(OmpInitClause); + MODIFIER_BOILERPLATE(OmpPreferType, OmpInteropType, OmpDepinfoModifier); + std::tuple t; +}; + // Ref: [5.0:170-176], [5.1:197-205], [5.2:138-139] // // in-reduction-clause -> @@ -5013,20 +5038,6 @@ struct OmpWhenClause { t; }; -// REF: [5.1:217-220], [5.2:293-294] -// -// init-clause -> INIT ([interop-modifier,] [interop-type,] -// interop-type: interop-var) -// interop-modifier: prefer_type(preference-list) -// interop-type: target, targetsync -// interop-var: Ompobject -// There can be at most only two interop-type. -struct OmpInitClause { - TUPLE_CLASS_BOILERPLATE(OmpInitClause); - MODIFIER_BOILERPLATE(OmpPreferType, OmpInteropType); - std::tuple t; -}; - // REF: [5.1:217-220], [5.2:294] // // 14.1.3 use-clause -> USE (interop-var) diff --git a/flang/include/flang/Semantics/openmp-modifiers.h b/flang/include/flang/Semantics/openmp-modifiers.h index de76255c7215c..8dcac9b179924 100644 --- a/flang/include/flang/Semantics/openmp-modifiers.h +++ b/flang/include/flang/Semantics/openmp-modifiers.h @@ -80,6 +80,7 @@ DECLARE_DESCRIPTOR(parser::OmpCloseModifier); DECLARE_DESCRIPTOR(parser::OmpContextSelector); DECLARE_DESCRIPTOR(parser::OmpDeleteModifier); DECLARE_DESCRIPTOR(parser::OmpDependenceType); +DECLARE_DESCRIPTOR(parser::OmpDepinfoModifier); DECLARE_DESCRIPTOR(parser::OmpDeviceModifier); DECLARE_DESCRIPTOR(parser::OmpDimsModifier); DECLARE_DESCRIPTOR(parser::OmpDirectiveNameModifier); diff --git a/flang/lib/Parser/openmp-parsers.cpp b/flang/lib/Parser/openmp-parsers.cpp index 1f0d1be9adc00..80962c211b2f5 100644 --- a/flang/lib/Parser/openmp-parsers.cpp +++ b/flang/lib/Parser/openmp-parsers.cpp @@ -252,6 +252,23 @@ void OmpDirectiveNameParser::initTokens(std::vector table[]) const { } } +// --- Common types --------------------------------------------------- + +TYPE_PARSER(construct( + "DEPOBJ" >> pure(common::OmpDependenceKind::Depobj) || + "IN"_id >> pure(common::OmpDependenceKind::In) || + "INOUT"_id >> pure(common::OmpDependenceKind::Inout) || + "INOUTSET" >> pure(common::OmpDependenceKind::Inoutset) || + "MUTEXINOUTSET" >> pure(common::OmpDependenceKind::Mutexinoutset) || + "OUT" >> pure(common::OmpDependenceKind::Out))) + +TYPE_PARSER(construct( + "ACQ_REL" >> pure(common::OmpMemoryOrderType::Acq_Rel) || + "ACQUIRE" >> pure(common::OmpMemoryOrderType::Acquire) || + "RELAXED" >> pure(common::OmpMemoryOrderType::Relaxed) || + "RELEASE" >> pure(common::OmpMemoryOrderType::Release) || + "SEQ_CST" >> pure(common::OmpMemoryOrderType::Seq_Cst))) + // --- Modifier helpers ----------------------------------------------- template struct ModifierList { @@ -816,6 +833,9 @@ TYPE_PARSER(construct( "SINK" >> pure(OmpDependenceType::Value::Sink) || "SOURCE" >> pure(OmpDependenceType::Value::Source))) +TYPE_PARSER(construct( + Parser{}, parenthesized(Parser{}))) + TYPE_PARSER(construct( "ANCESTOR" >> pure(OmpDeviceModifier::Value::Ancestor) || "DEVICE_NUM" >> pure(OmpDeviceModifier::Value::Device_Num))) @@ -998,7 +1018,8 @@ TYPE_PARSER(sourced( TYPE_PARSER(sourced( // Try interop-type first, since prefer-type can take arbitrary strings. construct(Parser{}) || - construct(Parser{}))) + construct(Parser{}) || + construct(Parser{}))) TYPE_PARSER(sourced(construct( Parser{}))) @@ -1105,11 +1126,7 @@ TYPE_PARSER(construct( // release // seq_cst TYPE_PARSER(construct( - "ACQ_REL" >> pure(common::OmpMemoryOrderType::Acq_Rel) || - "ACQUIRE" >> pure(common::OmpMemoryOrderType::Acquire) || - "RELAXED" >> pure(common::OmpMemoryOrderType::Relaxed) || - "RELEASE" >> pure(common::OmpMemoryOrderType::Release) || - "SEQ_CST" >> pure(common::OmpMemoryOrderType::Seq_Cst))) + Parser{})) TYPE_PARSER(construct( OmpDirectiveNameParser{}, maybe(parenthesized(scalarLogicalExpr)))) @@ -1138,12 +1155,7 @@ TYPE_PARSER(construct( maybe(nonemptyList(Parser{}) / ":"), Parser{})) -TYPE_PARSER(construct( - "ACQ_REL" >> pure(common::OmpMemoryOrderType::Acq_Rel) || - "ACQUIRE" >> pure(common::OmpMemoryOrderType::Acquire) || - "RELAXED" >> pure(common::OmpMemoryOrderType::Relaxed) || - "RELEASE" >> pure(common::OmpMemoryOrderType::Release) || - "SEQ_CST" >> pure(common::OmpMemoryOrderType::Seq_Cst))) +TYPE_PARSER(construct(Parser{})) TYPE_PARSER(construct(scalarIntExpr)) diff --git a/flang/lib/Parser/unparse.cpp b/flang/lib/Parser/unparse.cpp index 1fb03c28d10a3..1164dfd0bdf50 100644 --- a/flang/lib/Parser/unparse.cpp +++ b/flang/lib/Parser/unparse.cpp @@ -2232,6 +2232,12 @@ class UnparseVisitor { Walk(std::get>>(x.t), ": "); Walk(std::get(x.t)); } + void Unparse(const OmpDepinfoModifier &x) { + Walk(std::get(x.t)); + Put("("); + Walk(std::get(x.t)); + Put(")"); + } void Unparse(const OmpDetachClause &x) { Walk(x.v); } void Unparse(const OmpDeviceClause &x) { using Modifier = OmpDeviceClause::Modifier; diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp index edc6284544f5d..2acf0dee1f77e 100644 --- a/flang/lib/Semantics/check-omp-structure.cpp +++ b/flang/lib/Semantics/check-omp-structure.cpp @@ -1536,6 +1536,48 @@ void OmpStructureChecker::Leave(const parser::OmpDeclareVariantDirective &) { dirContext_.pop_back(); } +void OmpStructureChecker::CheckInitOnDepobj( + const parser::OpenMPDepobjConstruct &depobj, + const parser::OmpClause &initClause) { + const parser::OmpDirectiveSpecification &dirSpec{depobj.v}; + const parser::OmpArgumentList &args{dirSpec.Arguments()}; + const parser::OmpInitClause &init{ + std::get(initClause.u).v}; + + if (!args.v.empty()) { + context_.Say(args.source, + "The INIT clause is not allowed when the DEPOBJ directive has an argument"_err_en_US); + } + + if (!OmpVerifyModifiers( + init, llvm::omp::Clause::OMPC_init, initClause.source, context_)) { + return; + } + + auto &modifiers{OmpGetModifiers(init)}; + if (auto *depInfo{ + OmpGetUniqueModifier(modifiers)}) { + auto depKind{std::get(depInfo->t)}; + if (depKind == common::OmpDependenceKind::Depobj) { + auto &desc{OmpGetDescriptor()}; + context_.Say(OmpGetModifierSource(modifiers, depInfo), + "'%s' is not an allowed value of the '%s' modifier"_err_en_US, + parser::ToUpperCaseLetters(EnumToString(depKind)), desc.name.str()); + } + } else { + auto &desc{OmpGetDescriptor()}; + context_.Say(initClause.source, + "The '%s' modifier is required on a DEPOBJ construct"_err_en_US, + desc.name.str()); + } + if (auto *prefType{OmpGetUniqueModifier(modifiers)}) { + auto &desc{OmpGetDescriptor()}; + context_.Say(OmpGetModifierSource(modifiers, prefType), + "The '%s' modifier is not allowed on a DEPOBJ construct"_err_en_US, + desc.name.str()); + } +} + void OmpStructureChecker::Enter(const parser::OpenMPDepobjConstruct &x) { const auto &dirName{std::get(x.v.t)}; PushContextAndClauseSets(dirName.source, llvm::omp::Directive::OMPD_depobj); @@ -1560,45 +1602,49 @@ void OmpStructureChecker::Enter(const parser::OpenMPDepobjConstruct &x) { return; } - auto &clause{clauses.v.front()}; - if (version >= 60 && arguments.v.empty()) { context_.Say(x.source, "DEPOBJ syntax with no argument is not handled yet"_err_en_US); - return; } - // [5.2:73:27-28] - // If the destroy clause appears on a depobj construct, destroy-var must - // refer to the same depend object as the depobj argument of the construct. - if (clause.Id() == llvm::omp::Clause::OMPC_destroy) { - auto getObjSymbol{[&](const parser::OmpObject &obj) { - return common::visit( // - common::visitors{ - [&](auto &&s) { return GetLastName(s).symbol; }, - [&](const parser::OmpObject::Invalid &invalid) { - return static_cast(nullptr); - }, - }, - obj.u); - }}; - auto getArgSymbol{[&](const parser::OmpArgument &arg) { - if (auto *locator{std::get_if(&arg.u)}) { - if (auto *object{std::get_if(&locator->u)}) { - return getObjSymbol(*object); - } + auto getObjSymbol{[&](const parser::OmpObject &obj) { + return common::visit( // + common::visitors{ + [&](auto &&s) { return GetLastName(s).symbol; }, + [&](const parser::OmpObject::Invalid &invalid) { + return static_cast(nullptr); + }, + }, + obj.u); + }}; + auto getArgSymbol{[&](const parser::OmpArgument &arg) { + if (auto *locator{std::get_if(&arg.u)}) { + if (auto *object{std::get_if(&locator->u)}) { + return getObjSymbol(*object); } - return static_cast(nullptr); - }}; + } + return static_cast(nullptr); + }}; - auto &wrapper{std::get(clause.u)}; - if (const std::optional &destroy{wrapper.v}) { - const Symbol *constrSym{getArgSymbol(arguments.v.front())}; - const Symbol *clauseSym{getObjSymbol(destroy->v)}; - if (constrSym && clauseSym && constrSym != clauseSym) { - context_.Say(x.source, - "The DESTROY clause must refer to the same object as the " - "DEPOBJ construct"_err_en_US); + for (auto &clause : clauses.v) { + llvm::omp::Clause clauseId{clause.Id()}; + + if (clauseId == llvm::omp::Clause::OMPC_init) { + CheckInitOnDepobj(x, clause); + } else if (clauseId == llvm::omp::Clause::OMPC_destroy) { + // [5.2:73:27-28] + // If the destroy clause appears on a depobj construct, destroy-var must + // refer to the same depend object as the depobj argument of the + // construct. + auto &wrapper{std::get(clause.u)}; + if (const std::optional &destroy{wrapper.v}) { + const Symbol *constrSym{getArgSymbol(arguments.v.front())}; + const Symbol *clauseSym{getObjSymbol(destroy->v)}; + if (constrSym && clauseSym && constrSym != clauseSym) { + context_.Say(x.source, + "The DESTROY clause must refer to the same object as the " + "DEPOBJ construct"_err_en_US); + } } } } @@ -5484,6 +5530,14 @@ void OmpStructureChecker::Enter(const parser::OpenMPInteropConstruct &x) { ++targetCount; } } + if (auto *depInfo{ + OmpGetUniqueModifier( + modifiers)}) { + auto &desc{OmpGetDescriptor()}; + context_.Say(OmpGetModifierSource(modifiers, depInfo), + "The '%s' is not allowed on INTEROP construct"_err_en_US, + desc.name.str()); + } } const auto &interopVar{parser::Unwrap( std::get(initClause.v.t))}; diff --git a/flang/lib/Semantics/check-omp-structure.h b/flang/lib/Semantics/check-omp-structure.h index 4222259a56edb..9b5b0525dd27f 100644 --- a/flang/lib/Semantics/check-omp-structure.h +++ b/flang/lib/Semantics/check-omp-structure.h @@ -364,6 +364,8 @@ class OmpStructureChecker : public OmpStructureCheckerBase { const parser::OmpObjectList &ompObjectList); void CheckIfContiguous(const parser::OmpObject &object); const parser::Name *GetObjectName(const parser::OmpObject &object); + void CheckInitOnDepobj(const parser::OpenMPDepobjConstruct &depobj, + const parser::OmpClause &initClause); void CheckAllowedRequiresClause(llvmOmpClause clause); bool deviceConstructFound_{false}; diff --git a/flang/lib/Semantics/openmp-modifiers.cpp b/flang/lib/Semantics/openmp-modifiers.cpp index 12d857e4fc4ab..bf5a135c4ba00 100644 --- a/flang/lib/Semantics/openmp-modifiers.cpp +++ b/flang/lib/Semantics/openmp-modifiers.cpp @@ -288,6 +288,22 @@ const OmpModifierDescriptor &OmpGetDescriptor() { return desc; } +template <> +const OmpModifierDescriptor &OmpGetDescriptor() { + static const OmpModifierDescriptor desc{ + /*name=*/"depinfo-modifier", + /*props=*/ + { + {60, {OmpProperty::Unique}}, + }, + /*clauses=*/ + { + {60, {Clause::OMPC_init}}, + }, + }; + return desc; +} + template <> const OmpModifierDescriptor &OmpGetDescriptor() { static const OmpModifierDescriptor desc{ @@ -378,6 +394,7 @@ const OmpModifierDescriptor &OmpGetDescriptor() { /*props=*/ { {52, {OmpProperty::Required}}, + {60, {}}, }, /*clauses=*/ { diff --git a/flang/test/Parser/OpenMP/depobj-construct.f90 b/flang/test/Parser/OpenMP/depobj-construct.f90 index 1497414125aac..2d4831fe62bbb 100644 --- a/flang/test/Parser/OpenMP/depobj-construct.f90 +++ b/flang/test/Parser/OpenMP/depobj-construct.f90 @@ -1,5 +1,5 @@ -!RUN: %flang_fc1 -fdebug-unparse -fopenmp -fopenmp-version=52 %s | FileCheck --ignore-case --check-prefix="UNPARSE" %s -!RUN: %flang_fc1 -fdebug-dump-parse-tree -fopenmp -fopenmp-version=52 %s | FileCheck --check-prefix="PARSE-TREE" %s +!RUN: %flang_fc1 -fdebug-unparse-no-sema -fopenmp -fopenmp-version=60 %s | FileCheck --ignore-case --check-prefix="UNPARSE" %s +!RUN: %flang_fc1 -fdebug-dump-parse-tree-no-sema -fopenmp -fopenmp-version=60 %s | FileCheck --check-prefix="PARSE-TREE" %s subroutine f00 integer :: x, y @@ -62,3 +62,22 @@ subroutine f03 !PARSE-TREE: | OmpDirectiveName -> llvm::omp::Directive = depobj !PARSE-TREE: | OmpArgumentList -> OmpArgument -> OmpLocator -> OmpObject -> Designator -> DataRef -> Name = 'x' !PARSE-TREE: | OmpClauseList -> OmpClause -> Destroy -> + +subroutine f04 + integer :: x, y + !$omp depobj init(inoutset(x): y) +end + +!UNPARSE: SUBROUTINE f04 +!UNPARSE: INTEGER x, y +!UNPARSE: !$OMP DEPOBJ INIT(INOUTSET(x): y) +!UNPARSE: END SUBROUTINE + +!PARSE-TREE: ExecutionPartConstruct -> ExecutableConstruct -> OpenMPConstruct -> OpenMPStandaloneConstruct -> OpenMPDepobjConstruct -> OmpDirectiveSpecification +!PARSE-TREE: | OmpDirectiveName -> llvm::omp::Directive = depobj +!PARSE-TREE: | OmpClauseList -> OmpClause -> Init -> OmpInitClause +!PARSE-TREE: | | Modifier -> OmpDepinfoModifier +!PARSE-TREE: | | | OmpDependenceKind = Inoutset +!PARSE-TREE: | | | OmpObject -> Designator -> DataRef -> Name = 'x' +!PARSE-TREE: | | OmpObject -> Designator -> DataRef -> Name = 'y' +!PARSE-TREE: | Flags = {} diff --git a/flang/test/Semantics/OpenMP/init-clause.f90 b/flang/test/Semantics/OpenMP/init-clause.f90 new file mode 100644 index 0000000000000..e47b0af7d0aa6 --- /dev/null +++ b/flang/test/Semantics/OpenMP/init-clause.f90 @@ -0,0 +1,29 @@ +!RUN: %python %S/../test_errors.py %s %flang -fopenmp -fopenmp-version=60 + +subroutine f00 + integer :: x, y + !ERROR: The INIT clause is not allowed when the DEPOBJ directive has an argument + !ERROR: The 'depinfo-modifier' modifier is required on a DEPOBJ construct + !$omp depobj(x) init(y) +end + +subroutine f01 + integer :: x, y, z + !ERROR: DEPOBJ syntax with no argument is not handled yet + !ERROR: 'DEPOBJ' is not an allowed value of the 'depinfo-modifier' modifier + !$omp depobj init(depobj(y): z) +end + +subroutine f02 + integer :: x, y, z + !ERROR: DEPOBJ syntax with no argument is not handled yet + !ERROR: The 'depinfo-modifier' modifier is required on a DEPOBJ construct + !ERROR: The 'prefer-type' modifier is not allowed on a DEPOBJ construct + !$omp depobj init(prefer_type({fr("frid")}): z) +end + +subroutine f03 + integer :: x, y + !ERROR: The 'depinfo-modifier' is not allowed on INTEROP construct + !$omp interop init(mutexinoutset(x): y) +end diff --git a/llvm/include/llvm/Frontend/OpenMP/OMP.td b/llvm/include/llvm/Frontend/OpenMP/OMP.td index 5cd8f7374866e..391cddaa54676 100644 --- a/llvm/include/llvm/Frontend/OpenMP/OMP.td +++ b/llvm/include/llvm/Frontend/OpenMP/OMP.td @@ -853,6 +853,7 @@ def OMP_Depobj : Directive<[Spelling<"depobj">]> { // OMPKinds.def. VersionedClause, VersionedClause, + VersionedClause, VersionedClause, ]; let association = AS_None;