[flang-commits] [flang] bb6f16e - Revert "[flang][OpenMP] Reassociate ATOMIC update expressions (#153450)"
Krzysztof Parzyszek via flang-commits
flang-commits at lists.llvm.org
Wed Aug 13 10:17:23 PDT 2025
Author: Krzysztof Parzyszek
Date: 2025-08-13T12:17:13-05:00
New Revision: bb6f16ecd714ca8d5ffd326836c2f2bda2547926
URL: https://github.com/llvm/llvm-project/commit/bb6f16ecd714ca8d5ffd326836c2f2bda2547926
DIFF: https://github.com/llvm/llvm-project/commit/bb6f16ecd714ca8d5ffd326836c2f2bda2547926.diff
LOG: Revert "[flang][OpenMP] Reassociate ATOMIC update expressions (#153450)"
This reverts commit 7e125b9892f26d2028c3570f537e5a26f8c94447.
The fixes helped gcc-8, but not 7.5.
Added:
Modified:
flang/lib/Semantics/check-omp-atomic.cpp
flang/lib/Semantics/check-omp-structure.h
flang/test/Semantics/OpenMP/atomic-update-only.f90
flang/test/Semantics/OpenMP/atomic04.f90
Removed:
flang/test/Lower/OpenMP/atomic-update-reassoc.f90
################################################################################
diff --git a/flang/lib/Semantics/check-omp-atomic.cpp b/flang/lib/Semantics/check-omp-atomic.cpp
index 46985d1397cb8..0c0e6158485e9 100644
--- a/flang/lib/Semantics/check-omp-atomic.cpp
+++ b/flang/lib/Semantics/check-omp-atomic.cpp
@@ -13,9 +13,7 @@
#include "check-omp-structure.h"
#include "flang/Common/indirection.h"
-#include "flang/Common/template.h"
#include "flang/Evaluate/expression.h"
-#include "flang/Evaluate/match.h"
#include "flang/Evaluate/rewrite.h"
#include "flang/Evaluate/tools.h"
#include "flang/Parser/char-block.h"
@@ -52,127 +50,6 @@ static bool operator!=(const evaluate::Expr<T> &e, const evaluate::Expr<U> &f) {
return !(e == f);
}
-namespace {
-template <typename...> struct IsIntegral {
- static constexpr bool value{false};
-};
-
-template <common::TypeCategory C, int K>
-struct IsIntegral<evaluate::Type<C, K>> {
- static constexpr bool value{//
- C == common::TypeCategory::Integer ||
- C == common::TypeCategory::Unsigned ||
- C == common::TypeCategory::Logical};
-};
-
-template <typename T> constexpr bool is_integral_v{IsIntegral<T>::value};
-
-template <typename T, typename Op0, typename Op1>
-using ReassocOpBase = evaluate::match::AnyOfPattern< //
- evaluate::match::Add<T, Op0, Op1>, //
- evaluate::match::Mul<T, Op0, Op1>>;
-
-template <typename T, typename Op0, typename Op1>
-struct ReassocOp : public ReassocOpBase<T, Op0, Op1> {
- using Base = ReassocOpBase<T, Op0, Op1>;
- using Base::Base;
-};
-
-template <typename T, typename Op0, typename Op1>
-ReassocOp<T, Op0, Op1> reassocOp(const Op0 &op0, const Op1 &op1) {
- return ReassocOp<T, Op0, Op1>(op0, op1);
-}
-} // namespace
-
-struct ReassocRewriter : public evaluate::rewrite::Identity {
- using Id = evaluate::rewrite::Identity;
- using Id::operator();
- struct NonIntegralTag {};
-
- ReassocRewriter(const SomeExpr &atom) : atom_(atom) {}
-
- // Try to find cases where the input expression is of the form
- // (1) (a . b) . c, or
- // (2) a . (b . c),
- // where . denotes an associative operation (currently + or *), and a, b, c
- // are some subexpresions.
- // If one of the operands in the nested operation is the atomic variable
- // (with some possible type conversions applied to it), bring it to the
- // top-level operation, and move the top-level operand into the nested
- // operation.
- // For example, assuming x is the atomic variable:
- // (a + x) + b -> (a + b) + x, i.e. (conceptually) swap x and b.
- template <typename T, typename U,
- typename = std::enable_if_t<is_integral_v<T>>>
- evaluate::Expr<T> operator()(evaluate::Expr<T> &&x, const U &u) {
- // As per the above comment, there are 3 subexpressions involved in this
- // transformation. A match::Expr<T> will match evaluate::Expr<U> when T is
- // same as U, plus it will store a pointer (ref) to the matched expression.
- // When the match is successful, the sub[i].ref will point to a, b, x (in
- // some order) from the example above.
- evaluate::match::Expr<T> sub[3];
- auto inner{reassocOp<T>(sub[0], sub[1])};
- auto outer1{reassocOp<T>(inner, sub[2])}; // inner + something
- auto outer2{reassocOp<T>(sub[2], inner)}; // something + inner
- using MatchTypes = typename decltype(outer1)::MatchTypes;
- // There is no way to ensure that the outer operation is the same as
- // the inner one. They are matched independently, so we need to compare
- // the index in the member variant that represents the matched type.
- if ((match(outer1, x) && outer1.ref.index() == inner.ref.index()) ||
- (match(outer2, x) && outer2.ref.index() == inner.ref.index())) {
- size_t atomIdx{[&]() { // sub[atomIdx] will be the atom.
- size_t idx;
- for (idx = 0; idx != 3; ++idx) {
- if (IsAtom(*sub[idx].ref)) {
- break;
- }
- }
- return idx;
- }()};
-
- if (atomIdx > 2) {
- return Id::operator()(std::move(x), u);
- }
- return common::visit(
- [&](auto &&s) {
- using Expr = evaluate::Expr<T>;
- using TypeS = llvm::remove_cvref_t<decltype(s)>;
- // This visitor has to be semantically correct for all possible
- // types of s even though at runtime s will only be one of the
- // matched types.
- // Limit the construction to the operation types that we tried
- // to match (otherwise TypeS(op1, op2) would fail for non-binary
- // operations).
- if constexpr (common::HasMember<TypeS, MatchTypes>) {
- Expr atom{*sub[atomIdx].ref};
- Expr op1{*sub[(atomIdx + 1) % 3].ref};
- Expr op2{*sub[(atomIdx + 2) % 3].ref};
- return Expr(
- TypeS(atom, Expr(TypeS(std::move(op1), std::move(op2)))));
- } else {
- return Expr(TypeS(s));
- }
- },
- evaluate::match::deparen(x).u);
- }
- return Id::operator()(std::move(x), u);
- }
-
- template <typename T, typename U,
- typename = std::enable_if_t<!is_integral_v<T>>>
- evaluate::Expr<T> operator()(
- evaluate::Expr<T> &&x, const U &u, NonIntegralTag = {}) {
- return Id::operator()(std::move(x), u);
- }
-
-private:
- template <typename T> bool IsAtom(const evaluate::Expr<T> &x) const {
- return IsSameOrConvertOf(evaluate::AsGenericExpr(AsRvalue(x)), atom_);
- }
-
- const SomeExpr &atom_;
-};
-
struct AnalyzedCondStmt {
SomeExpr cond{evaluate::NullPointer{}}; // Default ctor is deleted
parser::CharBlock source;
@@ -322,26 +199,6 @@ static std::pair<parser::CharBlock, parser::CharBlock> SplitAssignmentSource(
llvm_unreachable("Could not find assignment operator");
}
-static std::vector<SomeExpr> GetNonAtomExpressions(
- const SomeExpr &atom, const std::vector<SomeExpr> &exprs) {
- std::vector<SomeExpr> nonAtom;
- for (const SomeExpr &e : exprs) {
- if (!IsSameOrConvertOf(e, atom)) {
- nonAtom.push_back(e);
- }
- }
- return nonAtom;
-}
-
-static std::vector<SomeExpr> GetNonAtomArguments(
- const SomeExpr &atom, const SomeExpr &expr) {
- if (auto &&maybe{GetConvertInput(expr)}) {
- return GetNonAtomExpressions(
- atom, GetTopLevelOperationIgnoreResizing(*maybe).second);
- }
- return {};
-}
-
static bool IsCheckForAssociated(const SomeExpr &cond) {
return GetTopLevelOperationIgnoreResizing(cond).first ==
operation::Operator::Associated;
@@ -718,7 +575,7 @@ OmpStructureChecker::CheckUpdateCapture(
void OmpStructureChecker::CheckAtomicCaptureAssignment(
const evaluate::Assignment &capture, const SomeExpr &atom,
parser::CharBlock source) {
- auto [_, rsrc]{SplitAssignmentSource(source)};
+ auto [lsrc, rsrc]{SplitAssignmentSource(source)};
const SomeExpr &cap{capture.lhs};
if (!IsVarOrFunctionRef(atom)) {
@@ -734,7 +591,7 @@ void OmpStructureChecker::CheckAtomicCaptureAssignment(
void OmpStructureChecker::CheckAtomicReadAssignment(
const evaluate::Assignment &read, parser::CharBlock source) {
- auto [_, rsrc]{SplitAssignmentSource(source)};
+ auto [lsrc, rsrc]{SplitAssignmentSource(source)};
if (auto maybe{GetConvertInput(read.rhs)}) {
const SomeExpr &atom{*maybe};
@@ -768,8 +625,7 @@ void OmpStructureChecker::CheckAtomicWriteAssignment(
}
}
-std::optional<evaluate::Assignment>
-OmpStructureChecker::CheckAtomicUpdateAssignment(
+void OmpStructureChecker::CheckAtomicUpdateAssignment(
const evaluate::Assignment &update, parser::CharBlock source) {
// [6.0:191:1-7]
// An update structured block is update-statement, an update statement
@@ -785,46 +641,14 @@ OmpStructureChecker::CheckAtomicUpdateAssignment(
if (!IsVarOrFunctionRef(atom)) {
ErrorShouldBeVariable(atom, rsrc);
// Skip other checks.
- return std::nullopt;
+ return;
}
CheckAtomicVariable(atom, lsrc);
- auto [hasErrors, tryReassoc]{CheckAtomicUpdateAssignmentRhs(
- atom, update.rhs, source, /*suppressDiagnostics=*/true)};
-
- if (!hasErrors) {
- CheckStorageOverlap(atom, GetNonAtomArguments(atom, update.rhs), source);
- return std::nullopt;
- } else if (tryReassoc) {
- ReassocRewriter ra(atom);
- SomeExpr raRhs{evaluate::rewrite::Mutator(ra)(update.rhs)};
-
- std::tie(hasErrors, tryReassoc) = CheckAtomicUpdateAssignmentRhs(
- atom, raRhs, source, /*suppressDiagnostics=*/true);
- if (!hasErrors) {
- CheckStorageOverlap(atom, GetNonAtomArguments(atom, raRhs), source);
-
- evaluate::Assignment raAssign(update);
- raAssign.rhs = raRhs;
- return raAssign;
- }
- }
-
- // This is guaranteed to report errors.
- CheckAtomicUpdateAssignmentRhs(
- atom, update.rhs, source, /*suppressDiagnostics=*/false);
- return std::nullopt;
-}
-
-std::pair<bool, bool> OmpStructureChecker::CheckAtomicUpdateAssignmentRhs(
- const SomeExpr &atom, const SomeExpr &rhs, parser::CharBlock source,
- bool suppressDiagnostics) {
- auto [_, rsrc]{SplitAssignmentSource(source)};
-
std::pair<operation::Operator, std::vector<SomeExpr>> top{
operation::Operator::Unknown, {}};
- if (auto &&maybeInput{GetConvertInput(rhs)}) {
+ if (auto &&maybeInput{GetConvertInput(update.rhs)}) {
top = GetTopLevelOperationIgnoreResizing(*maybeInput);
}
switch (top.first) {
@@ -841,39 +665,29 @@ std::pair<bool, bool> OmpStructureChecker::CheckAtomicUpdateAssignmentRhs(
case operation::Operator::Identity:
break;
case operation::Operator::Call:
- if (!suppressDiagnostics) {
- context_.Say(source,
- "A call to this function is not a valid ATOMIC UPDATE operation"_err_en_US);
- }
- return std::make_pair(true, false);
+ context_.Say(source,
+ "A call to this function is not a valid ATOMIC UPDATE operation"_err_en_US);
+ return;
case operation::Operator::Convert:
- if (!suppressDiagnostics) {
- context_.Say(source,
- "An implicit or explicit type conversion is not a valid ATOMIC UPDATE operation"_err_en_US);
- }
- return std::make_pair(true, false);
+ context_.Say(source,
+ "An implicit or explicit type conversion is not a valid ATOMIC UPDATE operation"_err_en_US);
+ return;
case operation::Operator::Intrinsic:
- if (!suppressDiagnostics) {
- context_.Say(source,
- "This intrinsic function is not a valid ATOMIC UPDATE operation"_err_en_US);
- }
- return std::make_pair(true, false);
+ context_.Say(source,
+ "This intrinsic function is not a valid ATOMIC UPDATE operation"_err_en_US);
+ return;
case operation::Operator::Constant:
case operation::Operator::Unknown:
- if (!suppressDiagnostics) {
- context_.Say(
- source, "This is not a valid ATOMIC UPDATE operation"_err_en_US);
- }
- return std::make_pair(true, false);
+ context_.Say(
+ source, "This is not a valid ATOMIC UPDATE operation"_err_en_US);
+ return;
default:
assert(
top.first != operation::Operator::Identity && "Handle this separately");
- if (!suppressDiagnostics) {
- context_.Say(source,
- "The %s operator is not a valid ATOMIC UPDATE operation"_err_en_US,
- operation::ToString(top.first));
- }
- return std::make_pair(true, false);
+ context_.Say(source,
+ "The %s operator is not a valid ATOMIC UPDATE operation"_err_en_US,
+ operation::ToString(top.first));
+ return;
}
// Check how many times `atom` occurs as an argument, if it's a subexpression
// of an argument, and collect the non-atom arguments.
@@ -894,48 +708,39 @@ std::pair<bool, bool> OmpStructureChecker::CheckAtomicUpdateAssignmentRhs(
return count;
}()};
- bool hasError{false}, tryReassoc{false};
+ bool hasError{false};
if (subExpr) {
- if (!suppressDiagnostics) {
- context_.Say(rsrc,
- "The atomic variable %s cannot be a proper subexpression of an argument (here: %s) in the update operation"_err_en_US,
- atom.AsFortran(), subExpr->AsFortran());
- }
+ context_.Say(rsrc,
+ "The atomic variable %s cannot be a proper subexpression of an argument (here: %s) in the update operation"_err_en_US,
+ atom.AsFortran(), subExpr->AsFortran());
hasError = true;
}
if (top.first == operation::Operator::Identity) {
// This is "x = y".
assert((atomCount == 0 || atomCount == 1) && "Unexpected count");
if (atomCount == 0) {
- if (!suppressDiagnostics) {
- context_.Say(rsrc,
- "The atomic variable %s should appear as an argument in the update operation"_err_en_US,
- atom.AsFortran());
- }
+ context_.Say(rsrc,
+ "The atomic variable %s should appear as an argument in the update operation"_err_en_US,
+ atom.AsFortran());
hasError = true;
}
} else {
if (atomCount == 0) {
- if (!suppressDiagnostics) {
- context_.Say(rsrc,
- "The atomic variable %s should appear as an argument of the top-level %s operator"_err_en_US,
- atom.AsFortran(), operation::ToString(top.first));
- }
- // If `atom` is a proper subexpression, and it not present as an
- // argument on its own, reassociation may be able to help.
- tryReassoc = subExpr.has_value();
+ context_.Say(rsrc,
+ "The atomic variable %s should appear as an argument of the top-level %s operator"_err_en_US,
+ atom.AsFortran(), operation::ToString(top.first));
hasError = true;
} else if (atomCount > 1) {
- if (!suppressDiagnostics) {
- context_.Say(rsrc,
- "The atomic variable %s should be exactly one of the arguments of the top-level %s operator"_err_en_US,
- atom.AsFortran(), operation::ToString(top.first));
- }
+ context_.Say(rsrc,
+ "The atomic variable %s should be exactly one of the arguments of the top-level %s operator"_err_en_US,
+ atom.AsFortran(), operation::ToString(top.first));
hasError = true;
}
}
- return std::make_pair(hasError, tryReassoc);
+ if (!hasError) {
+ CheckStorageOverlap(atom, nonAtom, source);
+ }
}
void OmpStructureChecker::CheckAtomicConditionalUpdateAssignment(
@@ -1038,13 +843,11 @@ void OmpStructureChecker::CheckAtomicUpdateOnly(
SourcedActionStmt action{GetActionStmt(&body.front())};
if (auto maybeUpdate{GetEvaluateAssignment(action.stmt)}) {
const SomeExpr &atom{maybeUpdate->lhs};
- auto maybeAssign{
- CheckAtomicUpdateAssignment(*maybeUpdate, action.source)};
- auto &updateAssign{maybeAssign.has_value() ? maybeAssign : maybeUpdate};
+ CheckAtomicUpdateAssignment(*maybeUpdate, action.source);
using Analysis = parser::OpenMPAtomicConstruct::Analysis;
x.analysis = AtomicAnalysis(atom)
- .addOp0(Analysis::Update, updateAssign)
+ .addOp0(Analysis::Update, maybeUpdate)
.addOp1(Analysis::None);
} else if (!IsAssignment(action.stmt)) {
context_.Say(
@@ -1160,19 +963,16 @@ void OmpStructureChecker::CheckAtomicUpdateCapture(
using Analysis = parser::OpenMPAtomicConstruct::Analysis;
int action;
- std::optional<evaluate::Assignment> updateAssign{update};
if (IsMaybeAtomicWrite(update)) {
action = Analysis::Write;
CheckAtomicWriteAssignment(update, uact.source);
} else {
action = Analysis::Update;
- if (auto &&maybe{CheckAtomicUpdateAssignment(update, uact.source)}) {
- updateAssign = maybe;
- }
+ CheckAtomicUpdateAssignment(update, uact.source);
}
CheckAtomicCaptureAssignment(capture, atom, cact.source);
- if (IsPointerAssignment(*updateAssign) != IsPointerAssignment(capture)) {
+ if (IsPointerAssignment(update) != IsPointerAssignment(capture)) {
context_.Say(cact.source,
"The update and capture assignments should both be pointer-assignments or both be non-pointer-assignments"_err_en_US);
return;
@@ -1180,12 +980,12 @@ void OmpStructureChecker::CheckAtomicUpdateCapture(
if (GetActionStmt(&body.front()).stmt == uact.stmt) {
x.analysis = AtomicAnalysis(atom)
- .addOp0(action, updateAssign)
+ .addOp0(action, update)
.addOp1(Analysis::Read, capture);
} else {
x.analysis = AtomicAnalysis(atom)
.addOp0(Analysis::Read, capture)
- .addOp1(action, updateAssign);
+ .addOp1(action, update);
}
}
diff --git a/flang/lib/Semantics/check-omp-structure.h b/flang/lib/Semantics/check-omp-structure.h
index a973aee28d0e2..6b33ca6ab583f 100644
--- a/flang/lib/Semantics/check-omp-structure.h
+++ b/flang/lib/Semantics/check-omp-structure.h
@@ -267,10 +267,8 @@ class OmpStructureChecker
const evaluate::Assignment &read, parser::CharBlock source);
void CheckAtomicWriteAssignment(
const evaluate::Assignment &write, parser::CharBlock source);
- std::optional<evaluate::Assignment> CheckAtomicUpdateAssignment(
+ void CheckAtomicUpdateAssignment(
const evaluate::Assignment &update, parser::CharBlock source);
- std::pair<bool, bool> CheckAtomicUpdateAssignmentRhs(const SomeExpr &atom,
- const SomeExpr &rhs, parser::CharBlock source, bool suppressDiagnostics);
void CheckAtomicConditionalUpdateAssignment(const SomeExpr &cond,
parser::CharBlock condSource, const evaluate::Assignment &assign,
parser::CharBlock assignSource);
diff --git a/flang/test/Lower/OpenMP/atomic-update-reassoc.f90 b/flang/test/Lower/OpenMP/atomic-update-reassoc.f90
deleted file mode 100644
index 96ebb56b8d6ca..0000000000000
--- a/flang/test/Lower/OpenMP/atomic-update-reassoc.f90
+++ /dev/null
@@ -1,75 +0,0 @@
-!RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=60 %s -o - | FileCheck %s
-
-subroutine f00(x, y)
- implicit none
- integer :: x, y
-
- !$omp atomic update
- x = ((x + 1) + y) + 2
-end
-
-!CHECK-LABEL: func.func @_QPf00
-!CHECK: %[[X:[0-9]+]]:2 = hlfir.declare %arg0
-!CHECK: %[[Y:[0-9]+]]:2 = hlfir.declare %arg1
-!CHECK: %c1_i32 = arith.constant 1 : i32
-!CHECK: %[[LOAD_Y:[0-9]+]] = fir.load %[[Y]]#0 : !fir.ref<i32>
-!CHECK: %[[Y_1:[0-9]+]] = arith.addi %c1_i32, %[[LOAD_Y]] : i32
-!CHECK: %c2_i32 = arith.constant 2 : i32
-!CHECK: %[[Y_1_2:[0-9]+]] = arith.addi %[[Y_1]], %c2_i32 : i32
-!CHECK: omp.atomic.update memory_order(relaxed) %[[X]]#0 : !fir.ref<i32> {
-!CHECK: ^bb0(%[[ARG:arg[0-9]+]]: i32):
-!CHECK: %[[ARG_P:[0-9]+]] = arith.addi %[[ARG]], %[[Y_1_2]] : i32
-!CHECK: omp.yield(%[[ARG_P]] : i32)
-!CHECK: }
-
-
-subroutine f01(x, y)
- implicit none
- real :: x
- integer :: y
-
- !$omp atomic update
- x = (int(x) + y) + 1
-end
-
-!CHECK-LABEL: func.func @_QPf01
-!CHECK: %[[X:[0-9]+]]:2 = hlfir.declare %arg0
-!CHECK: %[[Y:[0-9]+]]:2 = hlfir.declare %arg1
-!CHECK: %[[LOAD_Y:[0-9]+]] = fir.load %[[Y]]#0 : !fir.ref<i32>
-!CHECK: %c1_i32 = arith.constant 1 : i32
-!CHECK: %[[Y_1:[0-9]+]] = arith.addi %[[LOAD_Y]], %c1_i32 : i32
-!CHECK: omp.atomic.update memory_order(relaxed) %[[X]]#0 : !fir.ref<f32> {
-!CHECK: ^bb0(%[[ARG:arg[0-9]+]]: f32):
-!CHECK: %[[ARG_I:[0-9]+]] = fir.convert %[[ARG]] : (f32) -> i32
-!CHECK: %[[ARG_P:[0-9]+]] = arith.addi %[[ARG_I]], %[[Y_1]] : i32
-!CHECK: %[[ARG_F:[0-9]+]] = fir.convert %[[ARG_P]] : (i32) -> f32
-!CHECK: omp.yield(%[[ARG_F]] : f32)
-!CHECK: }
-
-
-subroutine f02(x, a, b, c)
- implicit none
- integer(kind=4) :: x
- integer(kind=8) :: a, b, c
-
- !$omp atomic update
- x = ((b + a) + x) + c
-end
-
-!CHECK-LABEL: func.func @_QPf02
-!CHECK: %[[A:[0-9]+]]:2 = hlfir.declare %arg1
-!CHECK: %[[B:[0-9]+]]:2 = hlfir.declare %arg2
-!CHECK: %[[C:[0-9]+]]:2 = hlfir.declare %arg3
-!CHECK: %[[X:[0-9]+]]:2 = hlfir.declare %arg0
-!CHECK: %[[LOAD_B:[0-9]+]] = fir.load %[[B]]#0 : !fir.ref<i64>
-!CHECK: %[[LOAD_A:[0-9]+]] = fir.load %[[A]]#0 : !fir.ref<i64>
-!CHECK: %[[A_B:[0-9]+]] = arith.addi %[[LOAD_B]], %[[LOAD_A]] : i64
-!CHECK: %[[LOAD_C:[0-9]+]] = fir.load %[[C]]#0 : !fir.ref<i64>
-!CHECK: %[[A_B_C:[0-9]+]] = arith.addi %[[A_B]], %[[LOAD_C]] : i64
-!CHECK: omp.atomic.update memory_order(relaxed) %[[X]]#0 : !fir.ref<i32> {
-!CHECK: ^bb0(%[[ARG:arg[0-9]+]]: i32):
-!CHECK: %[[ARG_8:[0-9]+]] = fir.convert %[[ARG]] : (i32) -> i64
-!CHECK: %[[ARG_P:[0-9]+]] = arith.addi %[[ARG_8]], %[[A_B_C]] : i64
-!CHECK: %[[ARG_4:[0-9]+]] = fir.convert %[[ARG_P]] : (i64) -> i32
-!CHECK: omp.yield(%[[ARG_4]] : i32)
-!CHECK: }
diff --git a/flang/test/Semantics/OpenMP/atomic-update-only.f90 b/flang/test/Semantics/OpenMP/atomic-update-only.f90
index 8ae261c463b00..3c027924a1423 100644
--- a/flang/test/Semantics/OpenMP/atomic-update-only.f90
+++ b/flang/test/Semantics/OpenMP/atomic-update-only.f90
@@ -28,18 +28,11 @@ subroutine f02
subroutine f03
integer :: x, y
- real :: xr, yr
- !With integer type the reassociation should be able to bring the `x` to
- !the top of the + operator. Expect no diagnostics.
!$omp atomic update
+ !ERROR: The atomic variable x cannot be a proper subexpression of an argument (here: (x+y)) in the update operation
+ !ERROR: The atomic variable x should appear as an argument of the top-level + operator
x = (x + y) + 1
-
- !Real variables cannot be reassociated (unless fastmath options are present).
- !$omp atomic update
- !ERROR: The atomic variable xr cannot be a proper subexpression of an argument (here: (xr+yr)) in the update operation
- !ERROR: The atomic variable xr should appear as an argument of the top-level + operator
- xr = (xr + yr) + 1
end
subroutine f04
diff --git a/flang/test/Semantics/OpenMP/atomic04.f90 b/flang/test/Semantics/OpenMP/atomic04.f90
index 002e06be68656..8f8af31245404 100644
--- a/flang/test/Semantics/OpenMP/atomic04.f90
+++ b/flang/test/Semantics/OpenMP/atomic04.f90
@@ -205,8 +205,9 @@ subroutine more_invalid_atomic_update_stmts()
!ERROR: The atomic variable a should appear as an argument of the top-level + operator
a = a * b + c
- !This is expected to work due to reassociation.
!$omp atomic update
+ !ERROR: The atomic variable a cannot be a proper subexpression of an argument (here: a+b) in the update operation
+ !ERROR: The atomic variable a should appear as an argument of the top-level + operator
a = a + b + c
!$omp atomic
More information about the flang-commits
mailing list