-
Notifications
You must be signed in to change notification settings - Fork 14.2k
[Flang][OpenMP] Add Semantic Checks for Atomic Capture Construct #108516
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@llvm/pr-subscribers-flang-fir-hlfir @llvm/pr-subscribers-flang-openmp Author: None (harishch4) ChangesThis PR adds semantic checks to ensure the atomic capture construct conforms to one of the valid forms: Functions checkForSymbolMatch and checkForSingleVariableOnRHS are moved from flang/lib/Lower/DirectivesCommon.h to flang/Semantics/tools.h for reuse. Full diff: https://github.com/llvm/llvm-project/pull/108516.diff 7 Files Affected:
diff --git a/flang/include/flang/Semantics/tools.h b/flang/include/flang/Semantics/tools.h
index 15c02ecc0058cc..b60514b991b727 100644
--- a/flang/include/flang/Semantics/tools.h
+++ b/flang/include/flang/Semantics/tools.h
@@ -736,5 +736,32 @@ std::string GetCommonBlockObjectName(const Symbol &, bool underscoring);
// Check for ambiguous USE associations
bool HadUseError(SemanticsContext &, SourceName at, const Symbol *);
+/// Checks if the assignment statement has a single variable on the RHS.
+inline bool checkForSingleVariableOnRHS(
+ const Fortran::parser::AssignmentStmt &assignmentStmt) {
+ const Fortran::parser::Expr &expr{
+ std::get<Fortran::parser::Expr>(assignmentStmt.t)};
+ const Fortran::common::Indirection<Fortran::parser::Designator> *designator =
+ std::get_if<Fortran::common::Indirection<Fortran::parser::Designator>>(
+ &expr.u);
+ return designator != nullptr;
+}
+
+/// Checks if the symbol on the LHS of the assignment statement is present in
+/// the RHS expression.
+inline bool checkForSymbolMatch(
+ const Fortran::parser::AssignmentStmt &assignmentStmt) {
+ const auto &var{std::get<Fortran::parser::Variable>(assignmentStmt.t)};
+ const auto &expr{std::get<Fortran::parser::Expr>(assignmentStmt.t)};
+ const auto *e{Fortran::semantics::GetExpr(expr)};
+ const auto *v{Fortran::semantics::GetExpr(var)};
+ auto varSyms{Fortran::evaluate::GetSymbolVector(*v)};
+ const Fortran::semantics::Symbol &varSymbol{*varSyms.front()};
+ for (const Fortran::semantics::Symbol &symbol :
+ Fortran::evaluate::GetSymbolVector(*e))
+ if (varSymbol == symbol)
+ return true;
+ return false;
+}
} // namespace Fortran::semantics
#endif // FORTRAN_SEMANTICS_TOOLS_H_
diff --git a/flang/lib/Lower/DirectivesCommon.h b/flang/lib/Lower/DirectivesCommon.h
index d2060e77ce5305..a32f0b287e049a 100644
--- a/flang/lib/Lower/DirectivesCommon.h
+++ b/flang/lib/Lower/DirectivesCommon.h
@@ -74,34 +74,6 @@ struct AddrAndBoundsInfo {
}
};
-/// Checks if the assignment statement has a single variable on the RHS.
-static inline bool checkForSingleVariableOnRHS(
- const Fortran::parser::AssignmentStmt &assignmentStmt) {
- const Fortran::parser::Expr &expr{
- std::get<Fortran::parser::Expr>(assignmentStmt.t)};
- const Fortran::common::Indirection<Fortran::parser::Designator> *designator =
- std::get_if<Fortran::common::Indirection<Fortran::parser::Designator>>(
- &expr.u);
- return designator != nullptr;
-}
-
-/// Checks if the symbol on the LHS of the assignment statement is present in
-/// the RHS expression.
-static inline bool
-checkForSymbolMatch(const Fortran::parser::AssignmentStmt &assignmentStmt) {
- const auto &var{std::get<Fortran::parser::Variable>(assignmentStmt.t)};
- const auto &expr{std::get<Fortran::parser::Expr>(assignmentStmt.t)};
- const auto *e{Fortran::semantics::GetExpr(expr)};
- const auto *v{Fortran::semantics::GetExpr(var)};
- auto varSyms{Fortran::evaluate::GetSymbolVector(*v)};
- const Fortran::semantics::Symbol &varSymbol{*varSyms.front()};
- for (const Fortran::semantics::Symbol &symbol :
- Fortran::evaluate::GetSymbolVector(*e))
- if (varSymbol == symbol)
- return true;
- return false;
-}
-
/// Populates \p hint and \p memoryOrder with appropriate clause information
/// if present on atomic construct.
static inline void genOmpAtomicHintAndMemoryOrderClauses(
@@ -537,7 +509,7 @@ void genOmpAccAtomicCapture(Fortran::lower::AbstractConverter &converter,
stmt2LHSArg = fir::getBase(converter.genExprAddr(assign2.lhs, stmtCtx));
// Operation specific RHS evaluations
- if (checkForSingleVariableOnRHS(stmt1)) {
+ if (Fortran::semantics::checkForSingleVariableOnRHS(stmt1)) {
// Atomic capture construct is of the form [capture-stmt, update-stmt] or
// of the form [capture-stmt, write-stmt]
stmt1RHSArg = fir::getBase(converter.genExprAddr(assign1.rhs, stmtCtx));
@@ -573,8 +545,8 @@ void genOmpAccAtomicCapture(Fortran::lower::AbstractConverter &converter,
firOpBuilder.createBlock(&(atomicCaptureOp->getRegion(0)));
mlir::Block &block = atomicCaptureOp->getRegion(0).back();
firOpBuilder.setInsertionPointToStart(&block);
- if (checkForSingleVariableOnRHS(stmt1)) {
- if (checkForSymbolMatch(stmt2)) {
+ if (Fortran::semantics::checkForSingleVariableOnRHS(stmt1)) {
+ if (Fortran::semantics::checkForSymbolMatch(stmt2)) {
// Atomic capture construct is of the form [capture-stmt, update-stmt]
const Fortran::semantics::SomeExpr &fromExpr =
*Fortran::semantics::GetExpr(stmt1Expr);
diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index 643b713b32e29d..662e6c10bd4e03 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -1977,6 +1977,70 @@ void OmpStructureChecker::CheckAtomicUpdateStmt(
ErrIfAllocatableVariable(var);
}
+void OmpStructureChecker::CheckAtomicCaptureConstruct(
+ const parser::OmpAtomicCapture &atomicCaptureConstruct) {
+ const Fortran::parser::AssignmentStmt &stmt1 =
+ std::get<Fortran::parser::OmpAtomicCapture::Stmt1>(
+ atomicCaptureConstruct.t)
+ .v.statement;
+ const auto &stmt1Var{std::get<Fortran::parser::Variable>(stmt1.t)};
+ const auto &stmt1Expr{std::get<Fortran::parser::Expr>(stmt1.t)};
+
+ const Fortran::parser::AssignmentStmt &stmt2 =
+ std::get<Fortran::parser::OmpAtomicCapture::Stmt2>(
+ atomicCaptureConstruct.t)
+ .v.statement;
+ const auto &stmt2Var{std::get<Fortran::parser::Variable>(stmt2.t)};
+ const auto &stmt2Expr{std::get<Fortran::parser::Expr>(stmt2.t)};
+
+ if (Fortran::semantics::checkForSingleVariableOnRHS(stmt1)) {
+ CheckAtomicCaptureStmt(stmt1);
+ if (Fortran::semantics::checkForSymbolMatch(stmt2)) {
+ // Atomic capture construct is of the form [capture-stmt, update-stmt]
+ CheckAtomicUpdateStmt(stmt2);
+ } else {
+ // Atomic capture construct is of the form [capture-stmt, write-stmt]
+ CheckAtomicWriteStmt(stmt2);
+ }
+ // Variable captured in stmt1 should be assigned in stmt2
+ const auto *e{GetExpr(context_, stmt1Expr)};
+ const auto *v{GetExpr(context_, stmt2Var)};
+ if (e && v) {
+ const Symbol &stmt2VarSymbol = evaluate::GetSymbolVector(*v).front();
+ const Symbol &stmt1ExprSymbol = evaluate::GetSymbolVector(*e).front();
+ if (stmt2VarSymbol != stmt1ExprSymbol)
+ context_.Say(stmt1Expr.source,
+ "Captured variable %s "
+ "expected to be assigned in statement 2 of "
+ "atomic capture construct"_err_en_US,
+ stmt1ExprSymbol.name().ToString());
+ }
+ } else if (Fortran::semantics::checkForSymbolMatch(stmt1) &&
+ Fortran::semantics::checkForSingleVariableOnRHS(stmt2)) {
+ // Atomic capture construct is of the form [update-stmt, capture-stmt]
+ CheckAtomicUpdateStmt(stmt1);
+ CheckAtomicCaptureStmt(stmt2);
+ // Variable updated in stmt1 should be captured in stmt2
+ const auto *e{GetExpr(context_, stmt2Expr)};
+ const auto *v{GetExpr(context_, stmt1Var)};
+ if (e && v) {
+ const Symbol &stmt1VarSymbol = evaluate::GetSymbolVector(*v).front();
+ const Symbol &stmt2ExprSymbol = evaluate::GetSymbolVector(*e).front();
+ if (stmt1VarSymbol != stmt2ExprSymbol)
+ context_.Say(stmt1Var.GetSource(),
+ "Updated variable %s "
+ "expected to be captured in statement 2 of "
+ "atomic capture construct"_err_en_US,
+ stmt1Var.GetSource().ToString());
+ }
+ } else {
+ context_.Say(stmt1Expr.source,
+ "Invalid atomic capture construct statements. "
+ "Expected one of [update-stmt, capture-stmt], "
+ "[capture-stmt, update-stmt], or [capture-stmt, write-stmt]"_err_en_US);
+ }
+}
+
void OmpStructureChecker::CheckAtomicMemoryOrderClause(
const parser::OmpAtomicClauseList *leftHandClauseList,
const parser::OmpAtomicClauseList *rightHandClauseList) {
@@ -2060,15 +2124,15 @@ void OmpStructureChecker::Enter(const parser::OpenMPAtomicConstruct &x) {
atomicWrite.t)
.statement);
},
- [&](const auto &atomicConstruct) {
- const auto &dir{std::get<parser::Verbatim>(atomicConstruct.t)};
+ [&](const parser::OmpAtomicCapture &atomicCapture) {
+ const auto &dir{std::get<parser::Verbatim>(atomicCapture.t)};
PushContextAndClauseSets(
dir.source, llvm::omp::Directive::OMPD_atomic);
- CheckAtomicMemoryOrderClause(&std::get<0>(atomicConstruct.t),
- &std::get<2>(atomicConstruct.t));
+ CheckAtomicMemoryOrderClause(
+ &std::get<0>(atomicCapture.t), &std::get<2>(atomicCapture.t));
CheckHintClause<const parser::OmpAtomicClauseList>(
- &std::get<0>(atomicConstruct.t),
- &std::get<2>(atomicConstruct.t));
+ &std::get<0>(atomicCapture.t), &std::get<2>(atomicCapture.t));
+ CheckAtomicCaptureConstruct(atomicCapture);
},
},
x.u);
diff --git a/flang/lib/Semantics/check-omp-structure.h b/flang/lib/Semantics/check-omp-structure.h
index 2cc1a78068f540..8bfd4d594b028e 100644
--- a/flang/lib/Semantics/check-omp-structure.h
+++ b/flang/lib/Semantics/check-omp-structure.h
@@ -193,6 +193,7 @@ class OmpStructureChecker
void CheckAtomicUpdateStmt(const parser::AssignmentStmt &);
void CheckAtomicCaptureStmt(const parser::AssignmentStmt &);
void CheckAtomicWriteStmt(const parser::AssignmentStmt &);
+ void CheckAtomicCaptureConstruct(const parser::OmpAtomicCapture &);
void CheckAtomicConstructStructure(const parser::OpenMPAtomicConstruct &);
void CheckDistLinear(const parser::OpenMPLoopConstruct &x);
void CheckSIMDNest(const parser::OpenMPConstruct &x);
diff --git a/flang/test/Semantics/OpenMP/omp-atomic-assignment-stmt.f90 b/flang/test/Semantics/OpenMP/omp-atomic-assignment-stmt.f90
index a346056dee383b..09921944e7fc06 100644
--- a/flang/test/Semantics/OpenMP/omp-atomic-assignment-stmt.f90
+++ b/flang/test/Semantics/OpenMP/omp-atomic-assignment-stmt.f90
@@ -84,4 +84,44 @@ program sample
!$omp atomic write
!ERROR: Expected scalar variable on the LHS of atomic assignment statement
a = x
+
+ !$omp atomic capture
+ v = x
+ x = x + 1
+ !$omp end atomic
+
+ !$omp atomic release capture
+ v = x
+ !ERROR: Atomic update statement should be of form `x = x operator expr` OR `x = expr operator x`
+ x = b + (x*1)
+ !$omp end atomic
+
+ !$omp atomic capture hint(0)
+ v = x
+ x = 1
+ !$omp end atomic
+
+ !$omp atomic capture
+ !ERROR: Captured variable x expected to be assigned in statement 2 of atomic capture construct
+ v = x
+ b = b + 1
+ !$omp end atomic
+
+ !$omp atomic capture
+ !ERROR: Captured variable x expected to be assigned in statement 2 of atomic capture construct
+ v = x
+ b = 10
+ !$omp end atomic
+
+ !$omp atomic capture
+ !ERROR: Updated variable x expected to be captured in statement 2 of atomic capture construct
+ x = x + 10
+ v = b
+ !$omp end atomic
+
+ !$omp atomic capture
+ !ERROR: Invalid atomic capture construct statements. Expected one of [update-stmt, capture-stmt], [capture-stmt, update-stmt], or [capture-stmt, write-stmt]
+ v = 1
+ x = 4
+ !$omp end atomic
end program
diff --git a/flang/test/Semantics/OpenMP/requires-atomic01.f90 b/flang/test/Semantics/OpenMP/requires-atomic01.f90
index b39c9cdcc0bb33..cb7b1bc1ac52ab 100644
--- a/flang/test/Semantics/OpenMP/requires-atomic01.f90
+++ b/flang/test/Semantics/OpenMP/requires-atomic01.f90
@@ -88,7 +88,7 @@ program requires
! CHECK: OmpMemoryOrderClause -> OmpClause -> SeqCst
!$omp atomic capture
i = j
- i = j
+ j = j + 1
!$omp end atomic
! CHECK-LABEL: OpenMPAtomicConstruct -> OmpAtomicCapture
@@ -96,7 +96,7 @@ program requires
! CHECK: OmpMemoryOrderClause -> OmpClause -> Relaxed
!$omp atomic relaxed capture
i = j
- i = j
+ j = j + 1
!$omp end atomic
! CHECK-LABEL: OpenMPAtomicConstruct -> OmpAtomicCapture
@@ -104,6 +104,6 @@ program requires
! CHECK: OmpMemoryOrderClause -> OmpClause -> Relaxed
!$omp atomic capture relaxed
i = j
- i = j
+ j = j + 1
!$omp end atomic
end program requires
diff --git a/flang/test/Semantics/OpenMP/requires-atomic02.f90 b/flang/test/Semantics/OpenMP/requires-atomic02.f90
index 3af83970e7927a..5a4249794f7b50 100644
--- a/flang/test/Semantics/OpenMP/requires-atomic02.f90
+++ b/flang/test/Semantics/OpenMP/requires-atomic02.f90
@@ -88,7 +88,7 @@ program requires
! CHECK: OmpMemoryOrderClause -> OmpClause -> AcqRel
!$omp atomic capture
i = j
- i = j
+ j = j + 1
!$omp end atomic
! CHECK-LABEL: OpenMPAtomicConstruct -> OmpAtomicCapture
@@ -96,7 +96,7 @@ program requires
! CHECK: OmpMemoryOrderClause -> OmpClause -> Relaxed
!$omp atomic relaxed capture
i = j
- i = j
+ j = j + 1
!$omp end atomic
! CHECK-LABEL: OpenMPAtomicConstruct -> OmpAtomicCapture
@@ -104,6 +104,6 @@ program requires
! CHECK: OmpMemoryOrderClause -> OmpClause -> Relaxed
!$omp atomic capture relaxed
i = j
- i = j
+ j = j + 1
!$omp end atomic
end program requires
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The 5.2 standard also appears to allow a conditional update statement as well as write or update. Is that handled here?
I am looking at section 4.3.1.3 page 83
No, this patch is based on the 5.0 standard. AFAIK flang-new doesn't support compare clause(5.1) fully yet. Please correct me if I'm wrong. |
If we can parse it then it is worth getting semantics right too. Otherwise adding a TODO in the code is okay. |
Missing parser support as well, added a TODO for now. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the change. This LGTM but please wait for another reviewer
for (const Fortran::semantics::Symbol &symbol : | ||
Fortran::evaluate::GetSymbolVector(*e)) | ||
if (varSymbol == symbol) | ||
return true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add braces in frontend code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
if (stmt2VarSymbol != stmt1ExprSymbol) | ||
context_.Say(stmt1Expr.source, | ||
"Captured variable %s " | ||
"expected to be assigned in statement 2 of " | ||
"atomic capture construct"_err_en_US, | ||
stmt1ExprSymbol.name().ToString()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add braces in frontend code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
Co-authored-by: Kiran Chandramohan <[email protected]>
Co-authored-by: Kiran Chandramohan <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Errors are not emitted for the following program. Is that expected?
program mn
type :: tp
real :: r1
real :: r2
end type
type(tp) :: r
real :: cr
integer :: v1(10), ci
!$omp atomic capture
cr = r%r1
r%r2 = r%r2 + 1.0
!$omp end atomic
!$omp atomic capture
ci = v1(2)
v1(1) = v1(1) + 1
!$omp end atomic
end
"Captured variable %s " | ||
"expected to be assigned in the second statement of " | ||
"atomic capture construct"_err_en_US, | ||
stmt1ExprSymbol.name().ToString()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ToString()
is not needed.
"Updated variable %s " | ||
"expected to be captured in the second statement of " | ||
"atomic capture construct"_err_en_US, | ||
stmt1Var.GetSource().ToString()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ToString()
is not needed.
!ERROR: Captured variable x expected to be assigned in statement 2 of atomic capture construct | ||
v = x | ||
b = b + 1 | ||
!$omp end atomic | ||
|
||
!$omp atomic capture | ||
!ERROR: Captured variable x expected to be assigned in statement 2 of atomic capture construct | ||
v = x | ||
b = 10 | ||
!$omp end atomic | ||
|
||
!$omp atomic capture | ||
!ERROR: Updated variable x expected to be captured in statement 2 of atomic capture construct |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ERROR messages have to be updated.
Thanks for the review. This is not expected, it looks like gfortran and ifx compilers are also not throwing an error. https://godbolt.org/z/sbYW9Tv3E |
I think it is the use of taking the first symbol from the SymbolVector that is leading to this situation. Could you check the lowering code to see what is used there to match the variable in the atomic update on the RHS? |
Even lowering is generating incorrect code, r1 is being used in atomic update instead of r2. Similarly V1(2) instead of V1(1) : https://godbolt.org/z/j4G18938n |
Usage of typed expressions should be able to handle this issue, is it? |
@kiranchandramohan I've added the given test cases and it's working as expected. Could you please review it? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the change. Minor comments inline. LGTM.
"Captured variable %s " | ||
"expected to be assigned in the second statement of " |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we have this in the same line so that the error message can be easily searched for?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For derived type components and array elements specifying them as variable is not probably accurate.
It is probably variable/array element/derived-type component.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we have this in the same line so that the error message can be easily searched for?
Wouldn't it violate the column width limit? Another error message is going to 170 columns. if that's okay, I'll change it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is alrite if it passes the code formatting checks. I think for strings in "" ""
, the code formatting is lenient.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the clarification. I've updated the error messages.
context_.Say(stmt1Expr.source, | ||
"Captured variable %s " | ||
"expected to be assigned in the second statement of " | ||
"atomic capture construct"_err_en_US, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"atomic capture construct"_err_en_US, | |
"ATOMIC CAPTURE construct"_err_en_US, |
This PR adds semantic checks to ensure the atomic capture construct conforms to one of the valid forms:
[capture-stmt, update-stmt], [capture-stmt, write-stmt] or [update-stmt, capture-stmt].
Functions checkForSymbolMatch and checkForSingleVariableOnRHS are moved from flang/lib/Lower/DirectivesCommon.h to flang/Semantics/tools.h for reuse.