|
12 | 12 | #include "flang/Parser/parse-tree.h"
|
13 | 13 | #include "flang/Semantics/expression.h"
|
14 | 14 | #include "flang/Semantics/tools.h"
|
| 15 | +#include <variant> |
15 | 16 |
|
16 | 17 | namespace Fortran::semantics {
|
17 | 18 |
|
@@ -746,62 +747,69 @@ void OmpStructureChecker::CheckSIMDNest(const parser::OpenMPConstruct &c) {
|
746 | 747 | // current context yet.
|
747 | 748 | // TODO: Check for declare simd regions.
|
748 | 749 | bool eligibleSIMD{false};
|
749 |
| - common::visit(Fortran::common::visitors{ |
750 |
| - // Allow `!$OMP ORDERED SIMD` |
751 |
| - [&](const parser::OpenMPBlockConstruct &c) { |
752 |
| - const auto &beginBlockDir{ |
753 |
| - std::get<parser::OmpBeginBlockDirective>(c.t)}; |
754 |
| - const auto &beginDir{ |
755 |
| - std::get<parser::OmpBlockDirective>(beginBlockDir.t)}; |
756 |
| - if (beginDir.v == llvm::omp::Directive::OMPD_ordered) { |
757 |
| - const auto &clauses{ |
758 |
| - std::get<parser::OmpClauseList>(beginBlockDir.t)}; |
759 |
| - for (const auto &clause : clauses.v) { |
760 |
| - if (std::get_if<parser::OmpClause::Simd>(&clause.u)) { |
761 |
| - eligibleSIMD = true; |
762 |
| - break; |
763 |
| - } |
764 |
| - } |
765 |
| - } |
766 |
| - }, |
767 |
| - [&](const parser::OpenMPSimpleStandaloneConstruct &c) { |
768 |
| - const auto &dir{ |
769 |
| - std::get<parser::OmpSimpleStandaloneDirective>(c.t)}; |
770 |
| - if (dir.v == llvm::omp::Directive::OMPD_ordered) { |
771 |
| - const auto &clauses{ |
772 |
| - std::get<parser::OmpClauseList>(c.t)}; |
773 |
| - for (const auto &clause : clauses.v) { |
774 |
| - if (std::get_if<parser::OmpClause::Simd>(&clause.u)) { |
775 |
| - eligibleSIMD = true; |
776 |
| - break; |
777 |
| - } |
778 |
| - } |
779 |
| - } |
780 |
| - }, |
781 |
| - // Allowing SIMD construct |
782 |
| - [&](const parser::OpenMPLoopConstruct &c) { |
783 |
| - const auto &beginLoopDir{ |
784 |
| - std::get<parser::OmpBeginLoopDirective>(c.t)}; |
785 |
| - const auto &beginDir{ |
786 |
| - std::get<parser::OmpLoopDirective>(beginLoopDir.t)}; |
787 |
| - if ((beginDir.v == llvm::omp::Directive::OMPD_simd) || |
788 |
| - (beginDir.v == llvm::omp::Directive::OMPD_do_simd)) { |
789 |
| - eligibleSIMD = true; |
790 |
| - } |
791 |
| - }, |
792 |
| - [&](const parser::OpenMPAtomicConstruct &c) { |
793 |
| - // Allow `!$OMP ATOMIC` |
794 |
| - eligibleSIMD = true; |
795 |
| - }, |
796 |
| - [&](const auto &c) {}, |
797 |
| - }, |
| 750 | + common::visit( |
| 751 | + Fortran::common::visitors{ |
| 752 | + // Allow `!$OMP ORDERED SIMD` |
| 753 | + [&](const parser::OpenMPBlockConstruct &c) { |
| 754 | + const auto &beginBlockDir{ |
| 755 | + std::get<parser::OmpBeginBlockDirective>(c.t)}; |
| 756 | + const auto &beginDir{ |
| 757 | + std::get<parser::OmpBlockDirective>(beginBlockDir.t)}; |
| 758 | + if (beginDir.v == llvm::omp::Directive::OMPD_ordered) { |
| 759 | + const auto &clauses{ |
| 760 | + std::get<parser::OmpClauseList>(beginBlockDir.t)}; |
| 761 | + for (const auto &clause : clauses.v) { |
| 762 | + if (std::get_if<parser::OmpClause::Simd>(&clause.u)) { |
| 763 | + eligibleSIMD = true; |
| 764 | + break; |
| 765 | + } |
| 766 | + } |
| 767 | + } |
| 768 | + }, |
| 769 | + [&](const parser::OpenMPStandaloneConstruct &c) { |
| 770 | + if (const auto &simpleConstruct = |
| 771 | + std::get_if<parser::OpenMPSimpleStandaloneConstruct>( |
| 772 | + &c.u)) { |
| 773 | + const auto &dir{std::get<parser::OmpSimpleStandaloneDirective>( |
| 774 | + simpleConstruct->t)}; |
| 775 | + if (dir.v == llvm::omp::Directive::OMPD_ordered) { |
| 776 | + const auto &clauses{ |
| 777 | + std::get<parser::OmpClauseList>(simpleConstruct->t)}; |
| 778 | + for (const auto &clause : clauses.v) { |
| 779 | + if (std::get_if<parser::OmpClause::Simd>(&clause.u)) { |
| 780 | + eligibleSIMD = true; |
| 781 | + break; |
| 782 | + } |
| 783 | + } |
| 784 | + } else if (dir.v == llvm::omp::Directive::OMPD_scan) { |
| 785 | + eligibleSIMD = true; |
| 786 | + } |
| 787 | + } |
| 788 | + }, |
| 789 | + // Allowing SIMD construct |
| 790 | + [&](const parser::OpenMPLoopConstruct &c) { |
| 791 | + const auto &beginLoopDir{ |
| 792 | + std::get<parser::OmpBeginLoopDirective>(c.t)}; |
| 793 | + const auto &beginDir{ |
| 794 | + std::get<parser::OmpLoopDirective>(beginLoopDir.t)}; |
| 795 | + if ((beginDir.v == llvm::omp::Directive::OMPD_simd) || |
| 796 | + (beginDir.v == llvm::omp::Directive::OMPD_do_simd)) { |
| 797 | + eligibleSIMD = true; |
| 798 | + } |
| 799 | + }, |
| 800 | + [&](const parser::OpenMPAtomicConstruct &c) { |
| 801 | + // Allow `!$OMP ATOMIC` |
| 802 | + eligibleSIMD = true; |
| 803 | + }, |
| 804 | + [&](const auto &c) {}, |
| 805 | + }, |
798 | 806 | c.u);
|
799 | 807 | if (!eligibleSIMD) {
|
800 | 808 | context_.Say(parser::FindSourceLocation(c),
|
801 | 809 | "The only OpenMP constructs that can be encountered during execution "
|
802 | 810 | "of a 'SIMD' region are the `ATOMIC` construct, the `LOOP` construct, "
|
803 |
| - "the `SIMD` construct and the `ORDERED` construct with the `SIMD` " |
804 |
| - "clause."_err_en_US); |
| 811 | + "the `SIMD` construct, the `SCAN` construct and the `ORDERED` " |
| 812 | + "construct with the `SIMD` clause."_err_en_US); |
805 | 813 | }
|
806 | 814 | }
|
807 | 815 |
|
@@ -965,6 +973,49 @@ void OmpStructureChecker::CheckDistLinear(
|
965 | 973 | }
|
966 | 974 |
|
967 | 975 | void OmpStructureChecker::Leave(const parser::OpenMPLoopConstruct &x) {
|
| 976 | + const auto &beginLoopDir{std::get<parser::OmpBeginLoopDirective>(x.t)}; |
| 977 | + const auto &clauseList{std::get<parser::OmpClauseList>(beginLoopDir.t)}; |
| 978 | + |
| 979 | + // A few semantic checks for InScan reduction are performed below as SCAN |
| 980 | + // constructs inside LOOP may add the relevant information. Scan reduction is |
| 981 | + // supported only in loop constructs, so same checks are not applicable to |
| 982 | + // other directives. |
| 983 | + for (const auto &clause : clauseList.v) { |
| 984 | + if (const auto *reductionClause{ |
| 985 | + std::get_if<parser::OmpClause::Reduction>(&clause.u)}) { |
| 986 | + const auto &maybeModifier{ |
| 987 | + std::get<std::optional<ReductionModifier>>(reductionClause->v.t)}; |
| 988 | + if (maybeModifier && *maybeModifier == ReductionModifier::Inscan) { |
| 989 | + const auto &objectList{ |
| 990 | + std::get<parser::OmpObjectList>(reductionClause->v.t)}; |
| 991 | + auto checkReductionSymbolInScan = [&](const parser::Name *name) { |
| 992 | + if (auto &symbol = name->symbol) { |
| 993 | + if (!symbol->test(Symbol::Flag::OmpInclusiveScan) && |
| 994 | + !symbol->test(Symbol::Flag::OmpExclusiveScan)) { |
| 995 | + context_.Say(name->source, |
| 996 | + "List item %s must appear in EXCLUSIVE or " |
| 997 | + "INCLUSIVE clause of an " |
| 998 | + "enclosed SCAN directive"_err_en_US, |
| 999 | + name->ToString()); |
| 1000 | + } |
| 1001 | + } |
| 1002 | + }; |
| 1003 | + for (const auto &ompObj : objectList.v) { |
| 1004 | + common::visit( |
| 1005 | + common::visitors{ |
| 1006 | + [&](const parser::Designator &designator) { |
| 1007 | + if (const auto *name{semantics::getDesignatorNameIfDataRef( |
| 1008 | + designator)}) { |
| 1009 | + checkReductionSymbolInScan(name); |
| 1010 | + } |
| 1011 | + }, |
| 1012 | + [&](const auto &name) { checkReductionSymbolInScan(&name); }, |
| 1013 | + }, |
| 1014 | + ompObj.u); |
| 1015 | + } |
| 1016 | + } |
| 1017 | + } |
| 1018 | + } |
968 | 1019 | if (llvm::omp::allSimdSet.test(GetContext().directive)) {
|
969 | 1020 | ExitDirectiveNest(SIMDNest);
|
970 | 1021 | }
|
@@ -1652,19 +1703,32 @@ void OmpStructureChecker::Leave(const parser::OpenMPAllocatorsConstruct &x) {
|
1652 | 1703 | dirContext_.pop_back();
|
1653 | 1704 | }
|
1654 | 1705 |
|
| 1706 | +void OmpStructureChecker::CheckScan( |
| 1707 | + const parser::OpenMPSimpleStandaloneConstruct &x) { |
| 1708 | + if (std::get<parser::OmpClauseList>(x.t).v.size() != 1) { |
| 1709 | + context_.Say(x.source, |
| 1710 | + "Exactly one of EXCLUSIVE or INCLUSIVE clause is expected"_err_en_US); |
| 1711 | + } |
| 1712 | + if (!CurrentDirectiveIsNested() || |
| 1713 | + !llvm::omp::scanParentAllowedSet.test(GetContextParent().directive)) { |
| 1714 | + context_.Say(x.source, |
| 1715 | + "Orphaned SCAN directives are prohibited; perhaps you forgot " |
| 1716 | + "to enclose the directive in to a WORKSHARING LOOP, a WORKSHARING " |
| 1717 | + "LOOP SIMD or a SIMD directive."_err_en_US); |
| 1718 | + } |
| 1719 | +} |
| 1720 | + |
1655 | 1721 | void OmpStructureChecker::CheckBarrierNesting(
|
1656 | 1722 | const parser::OpenMPSimpleStandaloneConstruct &x) {
|
1657 | 1723 | // A barrier region may not be `closely nested` inside a worksharing, loop,
|
1658 | 1724 | // task, taskloop, critical, ordered, atomic, or master region.
|
1659 | 1725 | // TODO: Expand the check to include `LOOP` construct as well when it is
|
1660 | 1726 | // supported.
|
1661 |
| - if (GetContext().directive == llvm::omp::Directive::OMPD_barrier) { |
1662 |
| - if (IsCloselyNestedRegion(llvm::omp::nestedBarrierErrSet)) { |
1663 |
| - context_.Say(parser::FindSourceLocation(x), |
1664 |
| - "`BARRIER` region may not be closely nested inside of `WORKSHARING`, " |
1665 |
| - "`LOOP`, `TASK`, `TASKLOOP`," |
1666 |
| - "`CRITICAL`, `ORDERED`, `ATOMIC` or `MASTER` region."_err_en_US); |
1667 |
| - } |
| 1727 | + if (IsCloselyNestedRegion(llvm::omp::nestedBarrierErrSet)) { |
| 1728 | + context_.Say(parser::FindSourceLocation(x), |
| 1729 | + "`BARRIER` region may not be closely nested inside of `WORKSHARING`, " |
| 1730 | + "`LOOP`, `TASK`, `TASKLOOP`," |
| 1731 | + "`CRITICAL`, `ORDERED`, `ATOMIC` or `MASTER` region."_err_en_US); |
1668 | 1732 | }
|
1669 | 1733 | }
|
1670 | 1734 |
|
@@ -1848,7 +1912,16 @@ void OmpStructureChecker::Enter(
|
1848 | 1912 | const parser::OpenMPSimpleStandaloneConstruct &x) {
|
1849 | 1913 | const auto &dir{std::get<parser::OmpSimpleStandaloneDirective>(x.t)};
|
1850 | 1914 | PushContextAndClauseSets(dir.source, dir.v);
|
1851 |
| - CheckBarrierNesting(x); |
| 1915 | + switch (dir.v) { |
| 1916 | + case llvm::omp::Directive::OMPD_barrier: |
| 1917 | + CheckBarrierNesting(x); |
| 1918 | + break; |
| 1919 | + case llvm::omp::Directive::OMPD_scan: |
| 1920 | + CheckScan(x); |
| 1921 | + break; |
| 1922 | + default: |
| 1923 | + break; |
| 1924 | + } |
1852 | 1925 | }
|
1853 | 1926 |
|
1854 | 1927 | void OmpStructureChecker::Leave(
|
@@ -2687,8 +2760,8 @@ CHECK_SIMPLE_CLAUSE(Full, OMPC_full)
|
2687 | 2760 | CHECK_SIMPLE_CLAUSE(Grainsize, OMPC_grainsize)
|
2688 | 2761 | CHECK_SIMPLE_CLAUSE(Hint, OMPC_hint)
|
2689 | 2762 | CHECK_SIMPLE_CLAUSE(Holds, OMPC_holds)
|
2690 |
| -CHECK_SIMPLE_CLAUSE(InReduction, OMPC_in_reduction) |
2691 | 2763 | CHECK_SIMPLE_CLAUSE(Inclusive, OMPC_inclusive)
|
| 2764 | +CHECK_SIMPLE_CLAUSE(InReduction, OMPC_in_reduction) |
2692 | 2765 | CHECK_SIMPLE_CLAUSE(Match, OMPC_match)
|
2693 | 2766 | CHECK_SIMPLE_CLAUSE(Nontemporal, OMPC_nontemporal)
|
2694 | 2767 | CHECK_SIMPLE_CLAUSE(NumTasks, OMPC_num_tasks)
|
@@ -2781,7 +2854,11 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Reduction &x) {
|
2781 | 2854 | if (CheckReductionOperators(x)) {
|
2782 | 2855 | CheckReductionTypeList(x);
|
2783 | 2856 | }
|
2784 |
| - CheckReductionModifier(x); |
| 2857 | + if (const auto &maybeModifier{ |
| 2858 | + std::get<std::optional<ReductionModifier>>(x.v.t)}) { |
| 2859 | + const ReductionModifier modifier{*maybeModifier}; |
| 2860 | + CheckReductionModifier(modifier); |
| 2861 | + } |
2785 | 2862 | }
|
2786 | 2863 |
|
2787 | 2864 | bool OmpStructureChecker::CheckReductionOperators(
|
@@ -2824,6 +2901,7 @@ bool OmpStructureChecker::CheckReductionOperators(
|
2824 | 2901 |
|
2825 | 2902 | return ok;
|
2826 | 2903 | }
|
| 2904 | + |
2827 | 2905 | bool OmpStructureChecker::CheckIntrinsicOperator(
|
2828 | 2906 | const parser::DefinedOperator::IntrinsicOperator &op) {
|
2829 | 2907 |
|
@@ -2958,14 +3036,11 @@ void OmpStructureChecker::CheckReductionTypeList(
|
2958 | 3036 | }
|
2959 | 3037 |
|
2960 | 3038 | void OmpStructureChecker::CheckReductionModifier(
|
2961 |
| - const parser::OmpClause::Reduction &x) { |
2962 |
| - using ReductionModifier = parser::OmpReductionClause::ReductionModifier; |
2963 |
| - const auto &maybeModifier{std::get<std::optional<ReductionModifier>>(x.v.t)}; |
2964 |
| - if (!maybeModifier || *maybeModifier == ReductionModifier::Default) { |
2965 |
| - // No modifier, or the default one is always ok. |
| 3039 | + const ReductionModifier &modifier) { |
| 3040 | + if (modifier == ReductionModifier::Default) { |
| 3041 | + // The default one is always ok. |
2966 | 3042 | return;
|
2967 | 3043 | }
|
2968 |
| - ReductionModifier modifier{*maybeModifier}; |
2969 | 3044 | const DirectiveContext &dirCtx{GetContext()};
|
2970 | 3045 | if (dirCtx.directive == llvm::omp::Directive::OMPD_loop) {
|
2971 | 3046 | // [5.2:257:33-34]
|
@@ -2996,15 +3071,10 @@ void OmpStructureChecker::CheckReductionModifier(
|
2996 | 3071 | // or "simd" directive.
|
2997 | 3072 | // The worksharing-loop directives are OMPD_do and OMPD_for. Only the
|
2998 | 3073 | // former is allowed in Fortran.
|
2999 |
| - switch (dirCtx.directive) { |
3000 |
| - case llvm::omp::Directive::OMPD_do: // worksharing-loop |
3001 |
| - case llvm::omp::Directive::OMPD_do_simd: // worksharing-loop simd |
3002 |
| - case llvm::omp::Directive::OMPD_simd: // "simd" |
3003 |
| - break; |
3004 |
| - default: |
| 3074 | + if (!llvm::omp::scanParentAllowedSet.test(dirCtx.directive)) { |
3005 | 3075 | context_.Say(GetContext().clauseSource,
|
3006 | 3076 | "Modifier 'INSCAN' on REDUCTION clause is only allowed with "
|
3007 |
| - "worksharing-loop, worksharing-loop simd, " |
| 3077 | + "WORKSHARING LOOP, WORKSHARING LOOP SIMD, " |
3008 | 3078 | "or SIMD directive"_err_en_US);
|
3009 | 3079 | }
|
3010 | 3080 | } else {
|
|
0 commit comments