diff --git a/flang/lib/Semantics/check-omp-structure.h b/flang/lib/Semantics/check-omp-structure.h --- a/flang/lib/Semantics/check-omp-structure.h +++ b/flang/lib/Semantics/check-omp-structure.h @@ -227,6 +227,7 @@ void CheckLoopItrVariableIsInt(const parser::OpenMPLoopConstruct &x); void CheckDoWhile(const parser::OpenMPLoopConstruct &x); void CheckCycleConstraints(const parser::OpenMPLoopConstruct &x); + void CheckOmpAtomicConstructStructure(const parser::OpenMPAtomicConstruct &); void CheckDistLinear(const parser::OpenMPLoopConstruct &x); void CheckSIMDNest(const parser::OpenMPConstruct &x); void CheckTargetNest(const parser::OpenMPConstruct &x); diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp --- a/flang/lib/Semantics/check-omp-structure.cpp +++ b/flang/lib/Semantics/check-omp-structure.cpp @@ -10,7 +10,6 @@ #include "flang/Parser/parse-tree.h" #include "flang/Semantics/tools.h" #include - namespace Fortran::semantics { // Use when clause falls under 'struct OmpClause' in 'parse-tree.h'. @@ -37,6 +36,94 @@ CheckAllowed(llvm::omp::Y); \ } +// 'OmpAtomicConstructChecker' is used to check the semantics of +// OpenMPAtomicConstruct +class OmpAtomicConstructChecker { +public: + OmpAtomicConstructChecker(SemanticsContext &context) : context_{context} {} + + template bool Pre(const T &) { return true; } + template void Post(const T &) {} + + bool Pre(const parser::AssignmentStmt &assignment) { + const auto &expr{std::get(assignment.t)}; + const auto &var{std::get(assignment.t)}; + std::visit( + common::visitors{ + [&](const common::Indirection &x) { + if (!CheckProcedureDesignatorValidity(x)) { + context_.Say(expr.source, + "Invalid intrinsic procedure name in OpenMP ATOMIC construct UPDATE statement"_err_en_US); + } + }, + [&](const auto &x) { + if (!CheckOperatorValidity(x, var)) { + context_.Say(expr.source, + "Invalid operator in OpenMP ATOMIC construct UPDATE statement"_err_en_US); + } + }, + }, + expr.u); + return true; + } + + template + void CompareNames(const T &operatorNode, const D &variable) { + const auto &variableName{variable.GetSource().ToString()}; + const auto &exprLeft{std::get<0>(operatorNode.t)}; + const auto &exprRight{std::get<1>(operatorNode.t)}; + if ((exprLeft.value().source.ToString() != variableName) && + (exprRight.value().source.ToString() != variableName)) { + context_.Say(variable.GetSource(), + "Variable name mismatch on lvalue and rvalue"_err_en_US); + } + } + + template bool CheckProcedureDesignatorValidity(const T &node) { + const auto &procedureDesignator{ + std::get(node.value().v.t)}; + return std::visit( + common::visitors{[&](const parser::Name &name) { + std::string procedureDesignatorName{name.ToString()}; + if (procedureDesignatorName == std::string{"iand"} || + procedureDesignatorName == std::string{"ior"} || + procedureDesignatorName == std::string{"ieor"} || + procedureDesignatorName == std::string{"max"} || + procedureDesignatorName == std::string{"min"}) { + return true; + } + return false; + }, + [&](const auto &) { return true; }}, + procedureDesignator.u); + } + template + bool CheckOperatorValidity(const T &node, const D &variable) { + if constexpr (common::HasMember) { + CompareNames(node, variable); + if constexpr (common::HasMember) { + return true; + } else { + return false; + } + } + return true; + } + +private: + SemanticsContext &context_; + using AllowedBinaryOperators = + std::variant; + using BinaryOperators = std::variant; +}; + // 'OmpWorkshareBlockChecker' is used to check the validity of the assignment // statements and the expressions enclosed in an OpenMP Workshare construct class OmpWorkshareBlockChecker { @@ -1202,11 +1289,32 @@ } } +void OmpStructureChecker::CheckOmpAtomicConstructStructure( + const parser::OpenMPAtomicConstruct &construct) { + std::visit( + common::visitors{ + [&](const auto &x) { + const auto &dir{std::get(x.t)}; + PushContextAndClauseSets( + dir.source, llvm::omp::Directive::OMPD_atomic); + OmpAtomicConstructChecker ompAtomicConstructChecker{context_}; + parser::Walk(x, ompAtomicConstructChecker); + }, + }, + construct.u); +} + void OmpStructureChecker::Enter(const parser::OpenMPAtomicConstruct &x) { std::visit( common::visitors{ - [&](const auto &someAtomicConstruct) { - const auto &dir{std::get(someAtomicConstruct.t)}; + [&](const parser::OmpAtomic &atomicConstruct) { + CheckOmpAtomicConstructStructure(x); + }, + [&](const parser::OmpAtomicUpdate &atomicConstruct) { + CheckOmpAtomicConstructStructure(x); + }, + [&](const auto &atomicConstruct) { + const auto &dir{std::get(atomicConstruct.t)}; PushContextAndClauseSets( dir.source, llvm::omp::Directive::OMPD_atomic); }, diff --git a/flang/test/Semantics/omp-atomic02.f90 b/flang/test/Semantics/omp-atomic02.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Semantics/omp-atomic02.f90 @@ -0,0 +1,113 @@ +! RUN: python %S/test_errors.py %s %flang -fopenmp + +! OpenMP Atomic construct +! section 2.17.7 +! operator is one of +, *, -, /, .AND., .OR., .EQV., or .NEQV + +program OmpAtomic + use omp_lib + CHARACTER c*3, d*3 + LOGICAL l, m, n + + a = 1 + b = 2 + c = 'foo' + d = 'bar' + m = .TRUE. + n = .FALSE. + !$omp parallel num_threads(4) + + !$omp atomic + a = a + (4*2) + !$omp atomic + a = a*(b + 1) + !$omp atomic + a = a - 3 + !$omp atomic + a = a/(b + 1) + !$omp atomic + !ERROR: Invalid operator in OpenMP ATOMIC construct UPDATE statement + a = a**4 + !$omp atomic + !ERROR: Invalid operator in OpenMP ATOMIC construct UPDATE statement + c = c//d + !$omp atomic + !ERROR: Variable name mismatch on lvalue and rvalue + !ERROR: Invalid operator in OpenMP ATOMIC construct UPDATE statement + l = a .LT. b + !$omp atomic + !ERROR: Variable name mismatch on lvalue and rvalue + !ERROR: Invalid operator in OpenMP ATOMIC construct UPDATE statement + l = a .LE. b + !$omp atomic + !ERROR: Variable name mismatch on lvalue and rvalue + !ERROR: Invalid operator in OpenMP ATOMIC construct UPDATE statement + l = a .EQ. b + !$omp atomic + !ERROR: Variable name mismatch on lvalue and rvalue + !ERROR: Invalid operator in OpenMP ATOMIC construct UPDATE statement + l = a .NE. b + !$omp atomic + !ERROR: Variable name mismatch on lvalue and rvalue + !ERROR: Invalid operator in OpenMP ATOMIC construct UPDATE statement + l = a .GE. b + !$omp atomic + !ERROR: Variable name mismatch on lvalue and rvalue + !ERROR: Invalid operator in OpenMP ATOMIC construct UPDATE statement + l = a .GT. b + !$omp atomic + m = m .AND. n + !$omp atomic + m = m .OR. n + !$omp atomic + m = m .EQV. n + !$omp atomic + m = m .NEQV. n + !$omp atomic update + a = a + (4*2) + !$omp atomic update + a = a*(b + 1) + !$omp atomic update + a = a - 3 + !$omp atomic update + a = a/(b + 1) + !$omp atomic update + !ERROR: Invalid operator in OpenMP ATOMIC construct UPDATE statement + a = a**4 + !$omp atomic update + !ERROR: Invalid operator in OpenMP ATOMIC construct UPDATE statement + c = c//d + !$omp atomic update + !ERROR: Variable name mismatch on lvalue and rvalue + !ERROR: Invalid operator in OpenMP ATOMIC construct UPDATE statement + l = a .LT. b + !$omp atomic update + !ERROR: Variable name mismatch on lvalue and rvalue + !ERROR: Invalid operator in OpenMP ATOMIC construct UPDATE statement + l = a .LE. b + !$omp atomic update + !ERROR: Variable name mismatch on lvalue and rvalue + !ERROR: Invalid operator in OpenMP ATOMIC construct UPDATE statement + l = a .EQ. b + !$omp atomic update + !ERROR: Variable name mismatch on lvalue and rvalue + !ERROR: Invalid operator in OpenMP ATOMIC construct UPDATE statement + l = a .NE. b + !$omp atomic update + !ERROR: Variable name mismatch on lvalue and rvalue + !ERROR: Invalid operator in OpenMP ATOMIC construct UPDATE statement + l = a .GE. b + !$omp atomic update + !ERROR: Variable name mismatch on lvalue and rvalue + !ERROR: Invalid operator in OpenMP ATOMIC construct UPDATE statement + l = a .GT. b + !$omp atomic update + m = m .AND. n + !$omp atomic update + m = m .OR. n + !$omp atomic update + m = m .EQV. n + !$omp atomic update + m = m .NEQV. n + !$omp end parallel +end program OmpAtomic diff --git a/flang/test/Semantics/omp-atomic03.f90 b/flang/test/Semantics/omp-atomic03.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Semantics/omp-atomic03.f90 @@ -0,0 +1,129 @@ +! RUN: python %S/test_errors.py %s %flang -fopenmp + +! OpenMP Atomic construct +! section 2.17.7 +! Intrinsic procedure name is one of MAX, MIN, IAND, IOR, or IEOR. + +program OmpAtomic + use omp_lib + real x + integer y + x = 5.73 + y = 3 +!$omp atomic + y = IAND(y, 4) +!$omp atomic + y = IOR(y, 5) +!$omp atomic + y = IEOR(y, 6) +!$omp atomic + y = MAX(y, 7) +!$omp atomic + y = MIN(y, 8) +!$omp atomic + !ERROR: Invalid intrinsic procedure name in OpenMP ATOMIC construct UPDATE statement + y = MOD(y, 9) +!$omp atomic + !ERROR: Invalid intrinsic procedure name in OpenMP ATOMIC construct UPDATE statement + x = ABS(x) +!$omp atomic + !ERROR: Invalid intrinsic procedure name in OpenMP ATOMIC construct UPDATE statement + x = SQRT(x) +!$omp atomic + !ERROR: Invalid intrinsic procedure name in OpenMP ATOMIC construct UPDATE statement + x = SIN(x) +!$omp atomic + !ERROR: Invalid intrinsic procedure name in OpenMP ATOMIC construct UPDATE statement + x = COS(x) +!$omp atomic + !ERROR: Invalid intrinsic procedure name in OpenMP ATOMIC construct UPDATE statement + x = TAN(x) +!$omp atomic + !ERROR: Invalid intrinsic procedure name in OpenMP ATOMIC construct UPDATE statement + x = ASIN(x) +!$omp atomic + !ERROR: Invalid intrinsic procedure name in OpenMP ATOMIC construct UPDATE statement + x = ACOS(x) +!$omp atomic + !ERROR: Invalid intrinsic procedure name in OpenMP ATOMIC construct UPDATE statement + x = ATAN(x) +!$omp atomic + !ERROR: Invalid intrinsic procedure name in OpenMP ATOMIC construct UPDATE statement + x = EXP(x) +!$omp atomic + !ERROR: Invalid intrinsic procedure name in OpenMP ATOMIC construct UPDATE statement + x = LOG(x) +!$omp atomic + !ERROR: Invalid intrinsic procedure name in OpenMP ATOMIC construct UPDATE statement + y = INT(x) +!$omp atomic + !ERROR: Invalid intrinsic procedure name in OpenMP ATOMIC construct UPDATE statement + y = NINT(x) +!$omp atomic + !ERROR: Invalid intrinsic procedure name in OpenMP ATOMIC construct UPDATE statement + y = FLOOR(x) +!$omp atomic + !ERROR: Invalid intrinsic procedure name in OpenMP ATOMIC construct UPDATE statement + y = FRACTION(x) +!$omp atomic + !ERROR: Invalid intrinsic procedure name in OpenMP ATOMIC construct UPDATE statement + y = REAL(x) +!$omp atomic update + y = IAND(y, 4) +!$omp atomic update + y = IOR(y, 5) +!$omp atomic update + y = IEOR(y, 6) +!$omp atomic update + y = MAX(y, 7) +!$omp atomic update + y = MIN(y, 8) +!$omp atomic update + !ERROR: Invalid intrinsic procedure name in OpenMP ATOMIC construct UPDATE statement + y = MOD(y, 9) +!$omp atomic update + !ERROR: Invalid intrinsic procedure name in OpenMP ATOMIC construct UPDATE statement + x = ABS(x) +!$omp atomic update + !ERROR: Invalid intrinsic procedure name in OpenMP ATOMIC construct UPDATE statement + x = SQRT(x) +!$omp atomic update + !ERROR: Invalid intrinsic procedure name in OpenMP ATOMIC construct UPDATE statement + x = SIN(x) +!$omp atomic update + !ERROR: Invalid intrinsic procedure name in OpenMP ATOMIC construct UPDATE statement + x = COS(x) +!$omp atomic update + !ERROR: Invalid intrinsic procedure name in OpenMP ATOMIC construct UPDATE statement + x = TAN(x) +!$omp atomic update + !ERROR: Invalid intrinsic procedure name in OpenMP ATOMIC construct UPDATE statement + x = ASIN(x) +!$omp atomic update + !ERROR: Invalid intrinsic procedure name in OpenMP ATOMIC construct UPDATE statement + x = ACOS(x) +!$omp atomic update + !ERROR: Invalid intrinsic procedure name in OpenMP ATOMIC construct UPDATE statement + x = ATAN(x) +!$omp atomic update + !ERROR: Invalid intrinsic procedure name in OpenMP ATOMIC construct UPDATE statement + x = EXP(x) +!$omp atomic update + !ERROR: Invalid intrinsic procedure name in OpenMP ATOMIC construct UPDATE statement + x = LOG(x) +!$omp atomic update + !ERROR: Invalid intrinsic procedure name in OpenMP ATOMIC construct UPDATE statement + y = INT(x) +!$omp atomic update + !ERROR: Invalid intrinsic procedure name in OpenMP ATOMIC construct UPDATE statement + y = NINT(x) +!$omp atomic update + !ERROR: Invalid intrinsic procedure name in OpenMP ATOMIC construct UPDATE statement + y = FLOOR(x) +!$omp atomic update + !ERROR: Invalid intrinsic procedure name in OpenMP ATOMIC construct UPDATE statement + y = FRACTION(x) +!$omp atomic update + !ERROR: Invalid intrinsic procedure name in OpenMP ATOMIC construct UPDATE statement + y = REAL(x) +end program OmpAtomic diff --git a/flang/test/Semantics/omp-atomic04.f90 b/flang/test/Semantics/omp-atomic04.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Semantics/omp-atomic04.f90 @@ -0,0 +1,168 @@ +! RUN: python %S/test_errors.py %s %flang -fopenmp + +! OpenMP Atomic construct +! section 2.17.7 +! Update assignment must be 'var = var op expr' or 'var = expr op var' + +program OmpAtomic + use omp_lib + real x + integer y + logical m, n, l + x = 5.73 + y = 3 + m = .TRUE. + n = .FALSE. +!$omp atomic + x = x + 1 +!$omp atomic + x = 1 + x +!$omp atomic + !ERROR: Variable name mismatch on lvalue and rvalue + x = y + 1 +!$omp atomic + !ERROR: Variable name mismatch on lvalue and rvalue + x = 1 + y + +!$omp atomic + x = x - 1 +!$omp atomic + x = 1 - x +!$omp atomic + !ERROR: Variable name mismatch on lvalue and rvalue + x = y - 1 +!$omp atomic + !ERROR: Variable name mismatch on lvalue and rvalue + x = 1 - y + +!$omp atomic + x = x*1 +!$omp atomic + x = 1*x +!$omp atomic + !ERROR: Variable name mismatch on lvalue and rvalue + x = y*1 +!$omp atomic + !ERROR: Variable name mismatch on lvalue and rvalue + x = 1*y + +!$omp atomic + x = x/1 +!$omp atomic + x = 1/x +!$omp atomic + !ERROR: Variable name mismatch on lvalue and rvalue + x = y/1 +!$omp atomic + !ERROR: Variable name mismatch on lvalue and rvalue + x = 1/y + +!$omp atomic + m = m .AND. n +!$omp atomic + m = n .AND. m +!$omp atomic + !ERROR: Variable name mismatch on lvalue and rvalue + m = n .AND. l + +!$omp atomic + m = m .OR. n +!$omp atomic + m = n .OR. m +!$omp atomic + !ERROR: Variable name mismatch on lvalue and rvalue + m = n .OR. l + +!$omp atomic + m = m .EQV. n +!$omp atomic + m = n .EQV. m +!$omp atomic + !ERROR: Variable name mismatch on lvalue and rvalue + m = n .EQV. l + +!$omp atomic + m = m .NEQV. n +!$omp atomic + m = n .NEQV. m +!$omp atomic + !ERROR: Variable name mismatch on lvalue and rvalue + m = n .NEQV. l + +!$omp atomic update + x = x + 1 +!$omp atomic update + x = 1 + x +!$omp atomic update + !ERROR: Variable name mismatch on lvalue and rvalue + x = y + 1 +!$omp atomic + !ERROR: Variable name mismatch on lvalue and rvalue + x = 1 + y + +!$omp atomic update + x = x - 1 +!$omp atomic update + x = 1 - x +!$omp atomic update + !ERROR: Variable name mismatch on lvalue and rvalue + x = y - 1 +!$omp atomic update + !ERROR: Variable name mismatch on lvalue and rvalue + x = 1 - y + +!$omp atomic update + x = x*1 +!$omp atomic update + x = 1*x +!$omp atomic update + !ERROR: Variable name mismatch on lvalue and rvalue + x = y*1 +!$omp atomic update + !ERROR: Variable name mismatch on lvalue and rvalue + x = 1*y + +!$omp atomic update + x = x/1 +!$omp atomic update + x = 1/x +!$omp atomic update + !ERROR: Variable name mismatch on lvalue and rvalue + x = y/1 +!$omp atomic update + !ERROR: Variable name mismatch on lvalue and rvalue + x = 1/y + +!$omp atomic update + m = m .AND. n +!$omp atomic update + m = n .AND. m +!$omp atomic update + !ERROR: Variable name mismatch on lvalue and rvalue + m = n .AND. l + +!$omp atomic update + m = m .OR. n +!$omp atomic update + m = n .OR. m +!$omp atomic update + !ERROR: Variable name mismatch on lvalue and rvalue + m = n .OR. l + +!$omp atomic update + m = m .EQV. n +!$omp atomic update + m = n .EQV. m +!$omp atomic update + !ERROR: Variable name mismatch on lvalue and rvalue + m = n .EQV. l + +!$omp atomic update + m = m .NEQV. n +!$omp atomic update + m = n .NEQV. m +!$omp atomic update + !ERROR: Variable name mismatch on lvalue and rvalue + m = n .NEQV. l + +end program OmpAtomic