Author: Andre Kuhlenschmidt Date: 2026-06-03T07:50:30-07:00 New Revision: fdf8fb6c13deb88c2e179a4eed0c66900dcd92d8
URL: https://github.com/llvm/llvm-project/commit/fdf8fb6c13deb88c2e179a4eed0c66900dcd92d8 DIFF: https://github.com/llvm/llvm-project/commit/fdf8fb6c13deb88c2e179a4eed0c66900dcd92d8.diff LOG: [flang][openacc] add extension which accepts multiple names in a OpenACC routine directive (#200296) This PR adds an extension which allows one or more function names in a single named routine directive. This is treated as multiple named routine directives with the same clauses. The bind clause is forbidden. The empty list of names isn't excepted. Routine clauses are stable under unparsing. This PR tests Parsing, Unparsing, Semantics, and Lowering. Added: flang/test/Lower/OpenACC/acc-module-definition-multi-name.f90 flang/test/Lower/OpenACC/acc-routine-multi-name.f90 flang/test/Parser/acc-routine-empty-parens.f90 flang/test/Semantics/OpenACC/acc-routine-multi-name-disabled.f90 flang/test/Semantics/OpenACC/acc-routine-multi-name.f90 Modified: clang/include/clang/Options/FlangOptions.td clang/lib/Driver/ToolChains/Flang.cpp flang/docs/OpenACC-extensions.md flang/include/flang/Parser/parse-tree.h flang/include/flang/Support/Fortran-features.h flang/lib/Frontend/CompilerInvocation.cpp flang/lib/Parser/openacc-parsers.cpp flang/lib/Parser/unparse.cpp flang/lib/Semantics/check-acc-structure.cpp flang/lib/Semantics/resolve-directives.cpp flang/lib/Support/Fortran-features.cpp flang/test/Parser/acc-unparse.f90 Removed: ################################################################################ diff --git a/clang/include/clang/Options/FlangOptions.td b/clang/include/clang/Options/FlangOptions.td index 41d908b3eb43e..26f584f918ca6 100644 --- a/clang/include/clang/Options/FlangOptions.td +++ b/clang/include/clang/Options/FlangOptions.td @@ -192,6 +192,9 @@ defm ppc_native_vec_elem_order: BoolOptionWithoutMarshalling<"f", "ppc-native-ve defm unsigned : OptInFC1FFlag<"unsigned", "Enables UNSIGNED type">; defm acc_allow_default_none_scalars : OptInFC1FFlag<"acc-allow-default-none-scalars", "Allow scalar variables in OpenACC default(none) regions without explicit data clauses (pre-OpenACC-3.2 behavior)">; +defm openacc_multiple_names_in_routine : OptOutFC1FFlag<"openacc-multiple-names-in-routine", + "Accept multiple names in OpenACC ROUTINE directive (extension)", + "Do not accept multiple names in OpenACC ROUTINE directive">; def fno_automatic : Flag<["-"], "fno-automatic">, Group<f_Group>, HelpText<"Implies the SAVE attribute for non-automatic local objects in subprograms unless RECURSIVE">; diff --git a/clang/lib/Driver/ToolChains/Flang.cpp b/clang/lib/Driver/ToolChains/Flang.cpp index 277264e6f133f..b0d494dfc9a51 100644 --- a/clang/lib/Driver/ToolChains/Flang.cpp +++ b/clang/lib/Driver/ToolChains/Flang.cpp @@ -138,6 +138,8 @@ void Flang::addDebugOptions(const llvm::opt::ArgList &Args, const JobAction &JA, options::OPT_funsigned, options::OPT_fno_unsigned, options::OPT_facc_allow_default_none_scalars, options::OPT_fno_acc_allow_default_none_scalars, + options::OPT_fopenacc_multiple_names_in_routine, + options::OPT_fno_openacc_multiple_names_in_routine, options::OPT_finstrument_functions}); llvm::codegenoptions::DebugInfoKind DebugInfoKind; diff --git a/flang/docs/OpenACC-extensions.md b/flang/docs/OpenACC-extensions.md index 52c5e5aebfee4..32b59233ae269 100644 --- a/flang/docs/OpenACC-extensions.md +++ b/flang/docs/OpenACC-extensions.md @@ -42,6 +42,19 @@ These extensions require no flag. in clauses of `!$acc declare` directives for a function, subroutine, program, or module, but Flang permits it with a warning when the same clause is used. +## Extensions enabled by default + +### `-fopenacc-multiple-names-in-routine` — `!$acc routine(<name>[, <name>]*) <clause-list>` + +The `ROUTINE` directive accepts a parenthesized list of more than one name +(e.g. `!$acc routine(foo, bar) seq`). The OpenACC specification permits only a +single name; this extension is equivalent to writing one `ROUTINE` directive +per name, each with identical clauses. A `BIND` clause may not be combined +with multiple names. A warning is emitted for each such directive +(`-Wopenacc-multiple-names-in-routine`; suppress with +`-Wno-openacc-multiple-names-in-routine`). This extension may be disabled with +`-fno-openacc-multiple-names-in-routine`. + ## Extensions enabled by flag ### `-facc-allow-default-none-scalars` — pre-OpenACC-3.2 scalar behavior under `DEFAULT(NONE)` diff --git a/flang/include/flang/Parser/parse-tree.h b/flang/include/flang/Parser/parse-tree.h index b8d622edf2b1a..7a073d83515e0 100644 --- a/flang/include/flang/Parser/parse-tree.h +++ b/flang/include/flang/Parser/parse-tree.h @@ -5644,7 +5644,7 @@ struct AccClauseList { struct OpenACCRoutineConstruct { TUPLE_CLASS_BOILERPLATE(OpenACCRoutineConstruct); CharBlock source; - std::tuple<Verbatim, std::optional<Name>, AccClauseList> t; + std::tuple<Verbatim, std::list<Name>, AccClauseList> t; }; struct OpenACCCacheConstruct { diff --git a/flang/include/flang/Support/Fortran-features.h b/flang/include/flang/Support/Fortran-features.h index b0ebc747157a3..db7f205f198cf 100644 --- a/flang/include/flang/Support/Fortran-features.h +++ b/flang/include/flang/Support/Fortran-features.h @@ -60,7 +60,7 @@ ENUM_CLASS(LanguageFeature, BackslashEscapes, OldDebugLines, DefaultStructConstructorNullPointer, AssumedRankIoItem, MultipleProgramUnitsOnSameLine, AllocatedForAssociated, OpenMPThreadprivateEquivalence, RelaxedCLoc, CudaPinned, - AccDefaultNoneScalars) + AccDefaultNoneScalars, OpenACCMultipleNamesInRoutine) // Portability and suspicious usage warnings ENUM_CLASS(UsageWarning, Portability, PointerToUndefinable, diff --git a/flang/lib/Frontend/CompilerInvocation.cpp b/flang/lib/Frontend/CompilerInvocation.cpp index f238c9a8aa551..25d52dd991e84 100644 --- a/flang/lib/Frontend/CompilerInvocation.cpp +++ b/flang/lib/Frontend/CompilerInvocation.cpp @@ -888,6 +888,13 @@ static bool parseFrontendArgs(FrontendOptions &opts, llvm::opt::ArgList &args, clang::options::OPT_fno_acc_allow_default_none_scalars, false)); + // -f{no-}openacc-multiple-names-in-routine + opts.features.Enable( + Fortran::common::LanguageFeature::OpenACCMultipleNamesInRoutine, + args.hasFlag(clang::options::OPT_fopenacc_multiple_names_in_routine, + clang::options::OPT_fno_openacc_multiple_names_in_routine, + true)); + // -f{no-}xor-operator opts.features.Enable(Fortran::common::LanguageFeature::XOROperator, args.hasFlag(clang::options::OPT_fxor_operator, diff --git a/flang/lib/Parser/openacc-parsers.cpp b/flang/lib/Parser/openacc-parsers.cpp index 0dec56521f750..6436b153fb208 100644 --- a/flang/lib/Parser/openacc-parsers.cpp +++ b/flang/lib/Parser/openacc-parsers.cpp @@ -176,8 +176,14 @@ TYPE_PARSER(construct<OpenACCLoopConstruct>( maybe(startAccLine >> Parser<AccEndLoop>{} / endAccLine))) // 2.15.1 Routine directive +// The name list is optional: empty list = unnamed/implicit form; 1+ names = +// named form. TYPE_PARSER(sourced(construct<OpenACCRoutineConstruct>(verbatim("ROUTINE"_tok), - maybe(parenthesized(name)), Parser<AccClauseList>{}))) + defaulted(localRecovery( + "empty parentheses in ROUTINE directive; omit parentheses for the unnamed form"_err_en_US, + !parenthesized(ok) >> parenthesized(nonemptyList(name)), + parenthesized(ok))), + Parser<AccClauseList>{}))) // 2.10 Cache directive TYPE_PARSER(sourced( diff --git a/flang/lib/Parser/unparse.cpp b/flang/lib/Parser/unparse.cpp index 58ecb365e138a..e5163c7ad3d54 100644 --- a/flang/lib/Parser/unparse.cpp +++ b/flang/lib/Parser/unparse.cpp @@ -2100,7 +2100,7 @@ class UnparseVisitor { void Unparse(const OpenACCRoutineConstruct &x) { BeginOpenACC(); Word("!$ACC ROUTINE"); - Walk("(", std::get<std::optional<Name>>(x.t), ")"); + Walk("(", std::get<std::list<Name>>(x.t), ",", ")"); Walk(std::get<AccClauseList>(x.t)); Put("\n"); EndOpenACC(); diff --git a/flang/lib/Semantics/check-acc-structure.cpp b/flang/lib/Semantics/check-acc-structure.cpp index 9f5a343f3848b..a684fe9e77647 100644 --- a/flang/lib/Semantics/check-acc-structure.cpp +++ b/flang/lib/Semantics/check-acc-structure.cpp @@ -480,8 +480,8 @@ void AccStructureChecker::Leave(const parser::OpenACCStandaloneConstruct &x) { void AccStructureChecker::Enter(const parser::OpenACCRoutineConstruct &x) { PushContextAndClauseSets(x.source, llvm::acc::Directive::ACCD_routine); - const auto &optName{std::get<std::optional<parser::Name>>(x.t)}; - if (!optName) { + const auto &names{std::get<std::list<parser::Name>>(x.t)}; + if (names.empty()) { const auto &verbatim{std::get<parser::Verbatim>(x.t)}; const auto &scope{context_.FindScope(verbatim.source)}; const Scope &containingScope{GetProgramUnitContaining(scope)}; diff --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp index 8c81b489d3f1f..4d9f266190ea3 100644 --- a/flang/lib/Semantics/resolve-directives.cpp +++ b/flang/lib/Semantics/resolve-directives.cpp @@ -1405,14 +1405,35 @@ bool AccAttributeVisitor::Pre(const parser::OpenACCRoutineConstruct &x) { } else { PushContext(verbatim.source, llvm::acc::Directive::ACCD_routine); } - if (const auto &optName{std::get<std::optional<parser::Name>>(x.t)}) { - if (Symbol * sym{ResolveFctName(*optName)}) { - Symbol &ultimate{sym->GetUltimate()}; - AddRoutineInfoToSymbol(ultimate, x); - } else { - context_.Say((*optName).source, - "No function or subroutine declared for '%s'"_err_en_US, - (*optName).source); + const auto &names{std::get<std::list<parser::Name>>(x.t)}; + if (!names.empty()) { + if (names.size() > 1) { + if (context_.IsEnabled( + common::LanguageFeature::OpenACCMultipleNamesInRoutine)) { + context_.Warn(common::LanguageFeature::OpenACCMultipleNamesInRoutine, + verbatim.source, + "OpenACC ROUTINE directive permits only a single name; multiple names accepted as an extension"_warn_en_US); + if (llvm::any_of(std::get<parser::AccClauseList>(x.t).v, + [](const parser::AccClause &c) { + return std::holds_alternative<parser::AccClause::Bind>(c.u); + })) { + context_.Say(verbatim.source, + "A BIND clause may only be specified when the ROUTINE directive refers to a single subroutine"_err_en_US); + } + } else { + context_.Say(verbatim.source, + "OpenACC ROUTINE directive does not permit multiple names"_err_en_US); + } + } + for (const auto &name : names) { + if (Symbol * sym{ResolveFctName(name)}) { + Symbol &ultimate{sym->GetUltimate()}; + AddRoutineInfoToSymbol(ultimate, x); + } else { + context_.Say(name.source, + "No function or subroutine declared for '%s'"_err_en_US, + name.source); + } } } else { if (currScope().symbol()) { diff --git a/flang/lib/Support/Fortran-features.cpp b/flang/lib/Support/Fortran-features.cpp index c656025ac04f5..b38b7b1a8b65e 100644 --- a/flang/lib/Support/Fortran-features.cpp +++ b/flang/lib/Support/Fortran-features.cpp @@ -219,6 +219,7 @@ LanguageFeatureControl::LanguageFeatureControl() { warnUsage_.set(UsageWarning::IgnoredNoReallocateLHS); warnUsage_.set(UsageWarning::CLoc); warnLanguage_.set(LanguageFeature::OpenMPThreadprivateEquivalence); + warnLanguage_.set(LanguageFeature::OpenACCMultipleNamesInRoutine); } std::optional<LanguageControlFlag> LanguageFeatureControl::FindWarning( diff --git a/flang/test/Lower/OpenACC/acc-module-definition-multi-name.f90 b/flang/test/Lower/OpenACC/acc-module-definition-multi-name.f90 new file mode 100644 index 0000000000000..0297284659cd4 --- /dev/null +++ b/flang/test/Lower/OpenACC/acc-module-definition-multi-name.f90 @@ -0,0 +1,44 @@ +! This file is compiled as a prerequisite by acc-routine-module-multi-name.f90 +! to generate mod_multi_name.mod. + +! RUN: rm -fr %t && mkdir -p %t && cd %t +! RUN: bbc -fopenacc -emit-fir %s +! RUN: cat mod_multi_name.mod | FileCheck %s + +!CHECK-LABEL: module mod_multi_name +module mod_multi_name + + ! Two-name form: seq1 and seq2 should both get !$acc routine seq in the mod. + !$acc routine(seq1, seq2) seq + + ! Three-name form: gang1, gang2, gang3 should all get !$acc routine gang. + !$acc routine(gang1, gang2, gang3) gang + +contains + + !CHECK-LABEL: subroutine seq1 + subroutine seq1() + !CHECK: !$acc routine seq + end subroutine + + !CHECK-LABEL: subroutine seq2 + subroutine seq2() + !CHECK: !$acc routine seq + end subroutine + + !CHECK-LABEL: subroutine gang1 + subroutine gang1() + !CHECK: !$acc routine gang + end subroutine + + !CHECK-LABEL: subroutine gang2 + subroutine gang2() + !CHECK: !$acc routine gang + end subroutine + + !CHECK-LABEL: subroutine gang3 + subroutine gang3() + !CHECK: !$acc routine gang + end subroutine + +end module diff --git a/flang/test/Lower/OpenACC/acc-routine-multi-name.f90 b/flang/test/Lower/OpenACC/acc-routine-multi-name.f90 new file mode 100644 index 0000000000000..6ba46f2da7fbb --- /dev/null +++ b/flang/test/Lower/OpenACC/acc-routine-multi-name.f90 @@ -0,0 +1,52 @@ +! This test checks that !$acc routine(name1, name2) and (name1, name2, name3) +! each produce one acc.routine op per named routine, equivalent to separate +! ROUTINE directives. + +! RUN: bbc -fopenacc -emit-hlfir %s -o - | FileCheck %s + +module acc_multi_routines + +! CHECK-DAG: acc.routine @[[r_seq2:.*]] func(@_QMacc_multi_routinesPseq2) seq +! CHECK-DAG: acc.routine @[[r_seq1:.*]] func(@_QMacc_multi_routinesPseq1) seq + + !$acc routine(seq1, seq2) seq + +! CHECK-DAG: acc.routine @[[r_gang3:.*]] func(@_QMacc_multi_routinesPgang3) gang +! CHECK-DAG: acc.routine @[[r_gang2:.*]] func(@_QMacc_multi_routinesPgang2) gang +! CHECK-DAG: acc.routine @[[r_gang1:.*]] func(@_QMacc_multi_routinesPgang1) gang + + !$acc routine(gang1, gang2, gang3) gang + +contains + + subroutine seq1() + end subroutine + +! CHECK-LABEL: func.func @_QMacc_multi_routinesPseq1() +! CHECK-SAME: attributes {acc.routine_info = #acc.routine_info<[@[[r_seq1]]]>} + + subroutine seq2() + end subroutine + +! CHECK-LABEL: func.func @_QMacc_multi_routinesPseq2() +! CHECK-SAME: attributes {acc.routine_info = #acc.routine_info<[@[[r_seq2]]]>} + + subroutine gang1() + end subroutine + +! CHECK-LABEL: func.func @_QMacc_multi_routinesPgang1() +! CHECK-SAME: attributes {acc.routine_info = #acc.routine_info<[@[[r_gang1]]]>} + + subroutine gang2() + end subroutine + +! CHECK-LABEL: func.func @_QMacc_multi_routinesPgang2() +! CHECK-SAME: attributes {acc.routine_info = #acc.routine_info<[@[[r_gang2]]]>} + + subroutine gang3() + end subroutine + +! CHECK-LABEL: func.func @_QMacc_multi_routinesPgang3() +! CHECK-SAME: attributes {acc.routine_info = #acc.routine_info<[@[[r_gang3]]]>} + +end module diff --git a/flang/test/Parser/acc-routine-empty-parens.f90 b/flang/test/Parser/acc-routine-empty-parens.f90 new file mode 100644 index 0000000000000..69771b50b64dc --- /dev/null +++ b/flang/test/Parser/acc-routine-empty-parens.f90 @@ -0,0 +1,55 @@ +! RUN: not %flang_fc1 -fopenacc -fsyntax-only %s 2>&1 | FileCheck %s + +! Check that !$acc routine() (empty parentheses) produces a specific error +! pointing at the empty parens, and that the parser recovers so that +! subsequent clauses (e.g. seq) are still parsed — no spurious +! "expected end of OpenACC directive" or "expected declaration construct" +! after recovery. + +subroutine sub1() +end subroutine + +module m + !$acc routine() seq +! CHECK: error: empty parentheses in ROUTINE directive; omit parentheses for the unnamed form + + !$acc routine() +! CHECK: error: empty parentheses in ROUTINE directive; omit parentheses for the unnamed form + +contains + subroutine inner() + !$acc routine() seq +! CHECK: error: empty parentheses in ROUTINE directive; omit parentheses for the unnamed form +! Verify the recovery did not leave "seq" or the () cases unconsumed. +! The range checked by these CHECK-NOTs extends from the third "empty +! parentheses" match above to the first CHECK below (the "expected '('" +! from the malformed-list section), so they cover the entire empty-parens +! section without bleeding into the intentional errors that follow. +! CHECK-NOT: empty parentheses in ROUTINE directive +! CHECK-NOT: expected declaration construct +! CHECK-NOT: expected end of OpenACC directive + end subroutine +end module + +! Malformed name-list cases: verify the "empty parentheses" recovery does not +! fire for inputs it does not apply to. A name written without parentheses +! and non-empty-but-malformed lists are left unconsumed by the ROUTINE parser +! and produce errors from the surrounding specification-part context instead. +! Each CHECK-NOT below is ranged between the surrounding CHECK lines so it +! tests only the interval between consecutive malformed-case errors. +module m2 +contains + subroutine sub_a() + end subroutine + subroutine sub_b() + !$acc routine sub_a seq +! CHECK: error: expected '(' +! CHECK-NOT: empty parentheses in ROUTINE directive + !$acc routine(sub_a,) seq +! CHECK: error: expected declaration construct +! CHECK-NOT: empty parentheses in ROUTINE directive + !$acc routine(sub_a sub_b) seq +! CHECK: error: expected declaration construct +! CHECK-NOT: empty parentheses in ROUTINE directive + end subroutine +end module diff --git a/flang/test/Parser/acc-unparse.f90 b/flang/test/Parser/acc-unparse.f90 index 12e6dec19f272..8ca72eee98786 100644 --- a/flang/test/Parser/acc-unparse.f90 +++ b/flang/test/Parser/acc-unparse.f90 @@ -86,3 +86,12 @@ subroutine routine2() !$acc routine(routine2) bind(routine2) ! CHECK: !$ACC ROUTINE(routine2) BIND(routine2) end subroutine + +subroutine routine3() +end subroutine + +module routine_multi_mod + ! Multi-name form: round-trips as-is (NV extension, not canonicalized). + !$acc routine(routine2, routine3) seq +! CHECK: !$ACC ROUTINE(routine2,routine3) SEQ +end module diff --git a/flang/test/Semantics/OpenACC/acc-routine-multi-name-disabled.f90 b/flang/test/Semantics/OpenACC/acc-routine-multi-name-disabled.f90 new file mode 100644 index 0000000000000..f6716494b8f55 --- /dev/null +++ b/flang/test/Semantics/OpenACC/acc-routine-multi-name-disabled.f90 @@ -0,0 +1,42 @@ +! RUN: %python %S/../test_errors.py %s %flang_fc1 -fopenacc -fno-openacc-multiple-names-in-routine + +! Check that -fno-openacc-multiple-names-in-routine turns the multiple-names +! extension into a hard error. A BIND clause with multiple names does not +! produce an additional diagnostic when the extension is disabled. + +subroutine sub1() +end subroutine + +subroutine sub2() +end subroutine + +subroutine sub3() +end subroutine + +subroutine sub4() +end subroutine + +subroutine sub5() +end subroutine + +subroutine sub6() +end subroutine + +module m + ! Two names with BIND: only the multiple-names error fires when disabled. + !ERROR: OpenACC ROUTINE directive does not permit multiple names + !$acc routine(sub1, sub2) seq bind(sub3) + + ! Three names: still one error per directive. + !ERROR: OpenACC ROUTINE directive does not permit multiple names + !$acc routine(sub3, sub4, sub5) gang + + ! Single name: no error. + !$acc routine(sub6) seq + +contains + subroutine inner() + ! Unnamed form must be inside a subroutine — no error. + !$acc routine seq + end subroutine +end module diff --git a/flang/test/Semantics/OpenACC/acc-routine-multi-name.f90 b/flang/test/Semantics/OpenACC/acc-routine-multi-name.f90 new file mode 100644 index 0000000000000..81eb4a78320ea --- /dev/null +++ b/flang/test/Semantics/OpenACC/acc-routine-multi-name.f90 @@ -0,0 +1,47 @@ +! RUN: %python %S/../test_errors.py %s %flang_fc1 -fopenacc + +! Check that !$acc routine(name1, name2) accepts multiple names with a +! warning (extension enabled by default), and that a BIND clause with +! multiple names is an error. + +subroutine sub1() +end subroutine + +subroutine sub2() +end subroutine + +subroutine sub3() +end subroutine + +subroutine sub4() +end subroutine + +subroutine sub5() +end subroutine + +subroutine sub6() +end subroutine + +module m + ! Two names: one warning per directive, both symbols get routine info. + !WARNING: OpenACC ROUTINE directive permits only a single name; multiple names accepted as an extension [-Wopenacc-multiple-names-in-routine] + !$acc routine(sub1, sub2) seq + + ! Three names: still one warning per directive, not per name. + !WARNING: OpenACC ROUTINE directive permits only a single name; multiple names accepted as an extension [-Wopenacc-multiple-names-in-routine] + !$acc routine(sub3, sub4, sub5) gang + + ! Single name: no warning. + !$acc routine(sub6) seq + + ! BIND clause with multiple names: error. + !WARNING: OpenACC ROUTINE directive permits only a single name; multiple names accepted as an extension [-Wopenacc-multiple-names-in-routine] + !ERROR: A BIND clause may only be specified when the ROUTINE directive refers to a single subroutine + !$acc routine(sub1, sub2) seq bind(sub3) + +contains + subroutine inner() + ! Unnamed form must be inside a subroutine — no warning. + !$acc routine seq + end subroutine +end module _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
