diff --git a/flang/lib/Lower/OpenMP.cpp b/flang/lib/Lower/OpenMP.cpp --- a/flang/lib/Lower/OpenMP.cpp +++ b/flang/lib/Lower/OpenMP.cpp @@ -1368,6 +1368,36 @@ } } +static bool checkForSingleVariableOnRHS( + const Fortran::parser::AssignmentStmt &assignmentStmt) { + // Check if the assignment statement has a single variable on the RHS + const Fortran::parser::Expr &expr{ + std::get(assignmentStmt.t)}; + const Fortran::common::Indirection *designator = + std::get_if>( + &expr.u); + const Fortran::parser::Name *name = + designator ? getDesignatorNameIfDataRef(designator->value()) : nullptr; + return name != nullptr; +} + +static bool +checkForSymbolMatch(const Fortran::parser::AssignmentStmt &assignmentStmt) { + // Check if the symbol on the LHS of the assignment statement is present in + // the RHS expression + const auto &var{std::get(assignmentStmt.t)}; + const auto &expr{std::get(assignmentStmt.t)}; + const auto *e{Fortran::semantics::GetExpr(expr)}; + const auto *v{Fortran::semantics::GetExpr(var)}; + const Fortran::semantics::Symbol &varSymbol = + Fortran::evaluate::GetSymbolVector(*v).front(); + for (const Fortran::semantics::Symbol &symbol : + Fortran::evaluate::GetSymbolVector(*e)) + if (varSymbol == symbol) + return true; + return false; +} + static void genOmpAtomicHintAndMemoryOrderClauses( Fortran::lower::AbstractConverter &converter, const Fortran::parser::OmpAtomicClauseList &clauseList, @@ -1406,20 +1436,65 @@ } } +static void genOmpAtomicCaptureStatement( + Fortran::lower::AbstractConverter &converter, + Fortran::lower::pft::Evaluation &eval, mlir::Value from_address, + mlir::Value to_address, + const Fortran::parser::OmpAtomicClauseList *leftHandClauseList, + const Fortran::parser::OmpAtomicClauseList *rightHandClauseList) { + // Generate `omp.atomic.read` operation for atomic assigment statements + auto &firOpBuilder = converter.getFirOpBuilder(); + auto currentLocation = converter.getCurrentLocation(); + + // If no hint clause is specified, the effect is as if + // hint(omp_sync_hint_none) had been specified. + mlir::IntegerAttr hint = nullptr; + mlir::omp::ClauseMemoryOrderKindAttr memory_order = nullptr; + if (leftHandClauseList) + genOmpAtomicHintAndMemoryOrderClauses(converter, *leftHandClauseList, hint, + memory_order); + if (rightHandClauseList) + genOmpAtomicHintAndMemoryOrderClauses(converter, *rightHandClauseList, hint, + memory_order); + firOpBuilder.create(currentLocation, from_address, + to_address, hint, memory_order); +} + +static void genOmpAtomicWriteStatement( + Fortran::lower::AbstractConverter &converter, + Fortran::lower::pft::Evaluation &eval, mlir::Value lhs_addr, + mlir::Value rhs_expr, + const Fortran::parser::OmpAtomicClauseList *leftHandClauseList, + const Fortran::parser::OmpAtomicClauseList *rightHandClauseList, + mlir::Value *evaluatedExprValue = nullptr) { + // Generate `omp.atomic.write` operation for atomic assignment statements + auto &firOpBuilder = converter.getFirOpBuilder(); + auto currentLocation = converter.getCurrentLocation(); + // If no hint clause is specified, the effect is as if + // hint(omp_sync_hint_none) had been specified. + mlir::IntegerAttr hint = nullptr; + mlir::omp::ClauseMemoryOrderKindAttr memory_order = nullptr; + if (leftHandClauseList) + genOmpAtomicHintAndMemoryOrderClauses(converter, *leftHandClauseList, hint, + memory_order); + if (rightHandClauseList) + genOmpAtomicHintAndMemoryOrderClauses(converter, *rightHandClauseList, hint, + memory_order); + firOpBuilder.create(currentLocation, lhs_addr, + rhs_expr, hint, memory_order); +} + static void genOmpAtomicUpdateStatement( Fortran::lower::AbstractConverter &converter, - Fortran::lower::pft::Evaluation &eval, - const Fortran::parser::Variable &assignmentStmtVariable, + Fortran::lower::pft::Evaluation &eval, mlir::Value lhs_addr, + mlir::Type varType, const Fortran::parser::Variable &assignmentStmtVariable, const Fortran::parser::Expr &assignmentStmtExpr, const Fortran::parser::OmpAtomicClauseList *leftHandClauseList, const Fortran::parser::OmpAtomicClauseList *rightHandClauseList) { // Generate `omp.atomic.update` operation for atomic assignment statements auto &firOpBuilder = converter.getFirOpBuilder(); auto currentLocation = converter.getCurrentLocation(); - Fortran::lower::StatementContext stmtCtx; - mlir::Value address = fir::getBase(converter.genExprAddr( - *Fortran::semantics::GetExpr(assignmentStmtVariable), stmtCtx)); // If no hint clause is specified, the effect is as if // hint(omp_sync_hint_none) had been specified. mlir::IntegerAttr hint = nullptr; @@ -1431,16 +1506,12 @@ genOmpAtomicHintAndMemoryOrderClauses(converter, *rightHandClauseList, hint, memory_order); auto atomicUpdateOp = firOpBuilder.create( - currentLocation, address, hint, memory_order); + currentLocation, lhs_addr, hint, memory_order); //// Generate body of Atomic Update operation // If an argument for the region is provided then create the block with that // argument. Also update the symbol's address with the argument mlir value. - mlir::Type varType = - fir::getBase( - converter.genExprValue( - *Fortran::semantics::GetExpr(assignmentStmtVariable), stmtCtx)) - .getType(); + SmallVector varTys = {varType}; SmallVector locs = {currentLocation}; firOpBuilder.createBlock(&atomicUpdateOp.getRegion(), {}, varTys, locs); @@ -1459,11 +1530,11 @@ // block. mlir::Block &block = atomicUpdateOp.getRegion().back(); firOpBuilder.setInsertionPointToEnd(&block); - - mlir::Value result = fir::getBase(converter.genExprValue( - *Fortran::semantics::GetExpr(assignmentStmtExpr), stmtCtx)); // Insert the terminator: YieldOp. - firOpBuilder.create(currentLocation, result); + Fortran::lower::StatementContext stmtCtx; + mlir::Value rhs_expr = fir::getBase(converter.genExprValue( + *Fortran::semantics::GetExpr(assignmentStmtExpr), stmtCtx)); + firOpBuilder.create(currentLocation, rhs_expr); // Reset the insert point to before the terminator. firOpBuilder.setInsertionPointToStart(&block); } @@ -1472,9 +1543,6 @@ genOmpAtomicWrite(Fortran::lower::AbstractConverter &converter, Fortran::lower::pft::Evaluation &eval, const Fortran::parser::OmpAtomicWrite &atomicWrite) { - auto &firOpBuilder = converter.getFirOpBuilder(); - auto currentLocation = converter.getCurrentLocation(); - // Get the value and address of atomic write operands. const Fortran::parser::OmpAtomicClauseList &rightHandClauseList = std::get<2>(atomicWrite.t); const Fortran::parser::OmpAtomicClauseList &leftHandClauseList = @@ -1483,29 +1551,19 @@ std::get(std::get<3>(atomicWrite.t).statement.t); const auto &assignmentStmtVariable = std::get( std::get<3>(atomicWrite.t).statement.t); + // Get the value and address of atomic write operands. Fortran::lower::StatementContext stmtCtx; - mlir::Value value = fir::getBase(converter.genExprValue( + mlir::Value rhs_expr = fir::getBase(converter.genExprValue( *Fortran::semantics::GetExpr(assignmentStmtExpr), stmtCtx)); - mlir::Value address = fir::getBase(converter.genExprAddr( + mlir::Value lhs_addr = fir::getBase(converter.genExprAddr( *Fortran::semantics::GetExpr(assignmentStmtVariable), stmtCtx)); - // If no hint clause is specified, the effect is as if - // hint(omp_sync_hint_none) had been specified. - mlir::IntegerAttr hint = nullptr; - mlir::omp::ClauseMemoryOrderKindAttr memory_order = nullptr; - genOmpAtomicHintAndMemoryOrderClauses(converter, leftHandClauseList, hint, - memory_order); - genOmpAtomicHintAndMemoryOrderClauses(converter, rightHandClauseList, hint, - memory_order); - firOpBuilder.create(currentLocation, address, value, - hint, memory_order); + genOmpAtomicWriteStatement(converter, eval, lhs_addr, rhs_expr, + &leftHandClauseList, &rightHandClauseList); } static void genOmpAtomicRead(Fortran::lower::AbstractConverter &converter, Fortran::lower::pft::Evaluation &eval, const Fortran::parser::OmpAtomicRead &atomicRead) { - auto &firOpBuilder = converter.getFirOpBuilder(); - auto currentLocation = converter.getCurrentLocation(); - // Get the address of atomic read operands. const Fortran::parser::OmpAtomicClauseList &rightHandClauseList = std::get<2>(atomicRead.t); const Fortran::parser::OmpAtomicClauseList &leftHandClauseList = @@ -1514,21 +1572,16 @@ std::get(std::get<3>(atomicRead.t).statement.t); const auto &assignmentStmtVariable = std::get( std::get<3>(atomicRead.t).statement.t); + // Get the address of atomic read operands. + Fortran::lower::StatementContext stmtCtx; mlir::Value from_address = fir::getBase(converter.genExprAddr( *Fortran::semantics::GetExpr(assignmentStmtExpr), stmtCtx)); mlir::Value to_address = fir::getBase(converter.genExprAddr( *Fortran::semantics::GetExpr(assignmentStmtVariable), stmtCtx)); - // If no hint clause is specified, the effect is as if - // hint(omp_sync_hint_none) had been specified. - mlir::IntegerAttr hint = nullptr; - mlir::omp::ClauseMemoryOrderKindAttr memory_order = nullptr; - genOmpAtomicHintAndMemoryOrderClauses(converter, leftHandClauseList, hint, - memory_order); - genOmpAtomicHintAndMemoryOrderClauses(converter, rightHandClauseList, hint, - memory_order); - firOpBuilder.create(currentLocation, from_address, - to_address, hint, memory_order); + + genOmpAtomicCaptureStatement(converter, eval, from_address, to_address, + &leftHandClauseList, &rightHandClauseList); } static void @@ -1543,10 +1596,18 @@ std::get(std::get<3>(atomicUpdate.t).statement.t); const auto &assignmentStmtVariable = std::get( std::get<3>(atomicUpdate.t).statement.t); + Fortran::lower::StatementContext stmtCtx; - genOmpAtomicUpdateStatement(converter, eval, assignmentStmtVariable, - assignmentStmtExpr, &leftHandClauseList, - &rightHandClauseList); + mlir::Value lhs_addr = fir::getBase(converter.genExprAddr( + *Fortran::semantics::GetExpr(assignmentStmtVariable), stmtCtx)); + mlir::Type varType = + fir::getBase( + converter.genExprValue( + *Fortran::semantics::GetExpr(assignmentStmtVariable), stmtCtx)) + .getType(); + genOmpAtomicUpdateStatement(converter, eval, lhs_addr, varType, + assignmentStmtVariable, assignmentStmtExpr, + &leftHandClauseList, &rightHandClauseList); } static void genOmpAtomic(Fortran::lower::AbstractConverter &converter, @@ -1562,10 +1623,126 @@ std::get>( atomicConstruct.t) .statement.t); + Fortran::lower::StatementContext stmtCtx; + mlir::Value lhs_addr = fir::getBase(converter.genExprAddr( + *Fortran::semantics::GetExpr(assignmentStmtVariable), stmtCtx)); + mlir::Type varType = + fir::getBase( + converter.genExprValue( + *Fortran::semantics::GetExpr(assignmentStmtVariable), stmtCtx)) + .getType(); + // If atomic-clause is not present on the construct, the behaviour is as if // the update clause is specified - genOmpAtomicUpdateStatement(converter, eval, assignmentStmtVariable, - assignmentStmtExpr, &atomicClauseList, nullptr); + genOmpAtomicUpdateStatement(converter, eval, lhs_addr, varType, + assignmentStmtVariable, assignmentStmtExpr, + &atomicClauseList, nullptr); +} + +static void +genOmpAtomicCapture(Fortran::lower::AbstractConverter &converter, + Fortran::lower::pft::Evaluation &eval, + const Fortran::parser::OmpAtomicCapture &atomicCapture) { + fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); + mlir::Location currentLocation = converter.getCurrentLocation(); + + mlir::IntegerAttr hint = nullptr; + mlir::omp::ClauseMemoryOrderKindAttr memory_order = nullptr; + const Fortran::parser::OmpAtomicClauseList &rightHandClauseList = + std::get<2>(atomicCapture.t); + const Fortran::parser::OmpAtomicClauseList &leftHandClauseList = + std::get<0>(atomicCapture.t); + genOmpAtomicHintAndMemoryOrderClauses(converter, leftHandClauseList, hint, + memory_order); + genOmpAtomicHintAndMemoryOrderClauses(converter, rightHandClauseList, hint, + memory_order); + + const Fortran::parser::AssignmentStmt &stmt1 = + std::get<3>(atomicCapture.t).v.statement; + const auto &stmt1Var{std::get(stmt1.t)}; + const auto &stmt1Expr{std::get(stmt1.t)}; + const Fortran::parser::AssignmentStmt &stmt2 = + std::get<4>(atomicCapture.t).v.statement; + const auto &stmt2Var{std::get(stmt2.t)}; + const auto &stmt2Expr{std::get(stmt2.t)}; + + // Pre-evaluate expressions to be used in the various operations inside + // `omp.atomic.capture` since it is not desirable to have anything other than + // a `omp.atomic.read`, `omp.atomic.write`, or `omp.atomic.update` operation + // inside `omp.atomic.capture` + Fortran::lower::StatementContext stmtCtx; + mlir::Value stmt1LHSArg, stmt1RHSArg, stmt2LHSArg, stmt2RHSArg; + // LHS evaluations are common to all combinations of `omp.atomic.capture` + stmt1LHSArg = fir::getBase( + converter.genExprAddr(*Fortran::semantics::GetExpr(stmt1Var), stmtCtx)); + stmt2LHSArg = fir::getBase( + converter.genExprAddr(*Fortran::semantics::GetExpr(stmt2Var), stmtCtx)); + + // Operation specific RHS evaluations + if (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( + *Fortran::semantics::GetExpr(stmt1Expr), stmtCtx)); + stmt2RHSArg = fir::getBase(converter.genExprValue( + *Fortran::semantics::GetExpr(stmt2Expr), stmtCtx)); + + } else { + // Atomic capture construct is of the form [update-stmt, capture-stmt] + stmt1RHSArg = fir::getBase(converter.genExprValue( + *Fortran::semantics::GetExpr(stmt1Expr), stmtCtx)); + stmt2RHSArg = fir::getBase(converter.genExprAddr( + *Fortran::semantics::GetExpr(stmt2Expr), stmtCtx)); + } + // Type information used in generation of `omp.atomic.update` operation + mlir::Type stmt1VarType = + fir::getBase(converter.genExprValue( + *Fortran::semantics::GetExpr(stmt1Var), stmtCtx)) + .getType(); + mlir::Type stmt2VarType = + fir::getBase(converter.genExprValue( + *Fortran::semantics::GetExpr(stmt2Var), stmtCtx)) + .getType(); + + auto atomicCaptureOp = firOpBuilder.create( + currentLocation, hint, memory_order); + firOpBuilder.createBlock(&atomicCaptureOp.getRegion()); + mlir::Block &block = atomicCaptureOp.getRegion().back(); + firOpBuilder.setInsertionPointToStart(&block); + if (checkForSingleVariableOnRHS(stmt1)) { + if (checkForSymbolMatch(stmt2)) { + // Atomic capture construct is of the form [capture-stmt, update-stmt] + genOmpAtomicCaptureStatement(converter, eval, stmt1RHSArg, stmt1LHSArg, + /*leftHandClauseList=*/nullptr, + /*rightHandClauseList=*/nullptr); + genOmpAtomicUpdateStatement(converter, eval, stmt1RHSArg, stmt2VarType, + stmt2Var, stmt2Expr, + /*leftHandClauseList=*/nullptr, + /*rightHandClauseList=*/nullptr); + } else { + // Atomic capture construct is of the form [capture-stmt, write-stmt] + genOmpAtomicCaptureStatement(converter, eval, stmt1RHSArg, stmt1LHSArg, + /*leftHandClauseList=*/nullptr, + /*rightHandClauseList=*/nullptr); + genOmpAtomicWriteStatement(converter, eval, stmt1RHSArg, stmt2RHSArg, + /*leftHandClauseList=*/nullptr, + /*rightHandClauseList=*/nullptr); + } + } else { + // Atomic capture construct is of the form [update-stmt, capture-stmt] + firOpBuilder.setInsertionPointToEnd(&block); + genOmpAtomicCaptureStatement(converter, eval, stmt1LHSArg, stmt2LHSArg, + /*leftHandClauseList=*/nullptr, + /*rightHandClauseList=*/nullptr); + firOpBuilder.setInsertionPointToStart(&block); + genOmpAtomicUpdateStatement(converter, eval, stmt1LHSArg, stmt1VarType, + stmt1Var, stmt1Expr, + /*leftHandClauseList=*/nullptr, + /*rightHandClauseList=*/nullptr); + } + firOpBuilder.setInsertionPointToEnd(&block); + firOpBuilder.create(currentLocation); + firOpBuilder.setInsertionPointToStart(&block); } static void @@ -1585,8 +1762,8 @@ [&](const Fortran::parser::OmpAtomicUpdate &atomicUpdate) { genOmpAtomicUpdate(converter, eval, atomicUpdate); }, - [&](const auto &) { - TODO(converter.getCurrentLocation(), "Atomic capture"); + [&](const Fortran::parser::OmpAtomicCapture &atomicCapture) { + genOmpAtomicCapture(converter, eval, atomicCapture); }, }, atomicConstruct.u); diff --git a/flang/test/Lower/OpenMP/atomic-capture.f90 b/flang/test/Lower/OpenMP/atomic-capture.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Lower/OpenMP/atomic-capture.f90 @@ -0,0 +1,133 @@ +! RUN: bbc -fopenmp -emit-fir %s -o - | FileCheck %s +! RUN: %flang_fc1 -emit-fir -fopenmp %s -o - | FileCheck %s + +! This test checks the lowering of atomic capture + +program OmpAtomicCapture + use omp_lib + integer :: x, y + +!CHECK: %[[X:.*]] = fir.alloca i32 {bindc_name = "x", uniq_name = "_QFEx"} +!CHECK: %[[Y:.*]] = fir.alloca i32 {bindc_name = "y", uniq_name = "_QFEy"} +!CHECK: omp.atomic.capture memory_order(release) { +!CHECK: omp.atomic.read %[[X]] = %[[Y]] : !fir.ref +!CHECK: omp.atomic.update %[[Y]] : !fir.ref { +!CHECK: ^bb0(%[[ARG:.*]]: i32): +!CHECK: %[[temp:.*]] = fir.load %[[X]] : !fir.ref +!CHECK: %[[result:.*]] = arith.addi %[[temp]], %[[ARG]] : i32 +!CHECK: omp.yield(%[[result]] : i32) +!CHECK: } +!CHECK: } + + !$omp atomic capture release + x = y + y = x + y + !$omp end atomic + + +!CHECK: omp.atomic.capture hint(uncontended) { +!CHECK: omp.atomic.update %[[Y]] : !fir.ref { +!CHECK: ^bb0(%[[ARG:.*]]: i32): +!CHECK: %[[temp:.*]] = fir.load %[[X]] : !fir.ref +!CHECK: %[[result:.*]] = arith.muli %[[temp]], %[[ARG]] : i32 +!CHECK: omp.yield(%[[result]] : i32) +!CHECK: } +!CHECK: omp.atomic.read %[[X]] = %[[Y]] : !fir.ref +!CHECK: } + + !$omp atomic hint(omp_sync_hint_uncontended) capture + y = x * y + x = y + !$omp end atomic + +!CHECK: %[[constant_20:.*]] = arith.constant 20 : i32 +!CHECK: %[[constant_8:.*]] = arith.constant 8 : i32 +!CHECK: %[[temp:.*]] = fir.load %[[X]] : !fir.ref +!CHECK: %[[result:.*]] = arith.subi %[[constant_8]], %[[temp]] : i32 +!CHECK: %[[result_noreassoc:.*]] = fir.no_reassoc %[[result]] : i32 +!CHECK: %[[result:.*]] = arith.addi %[[constant_20]], %[[result_noreassoc]] : i32 +!CHECK: omp.atomic.capture memory_order(acquire) hint(nonspeculative) { +!CHECK: omp.atomic.read %[[X]] = %[[Y]] : !fir.ref +!CHECK: omp.atomic.write %[[Y]] = %[[result]] : !fir.ref, i32 +!CHECK: } + + !$omp atomic hint(omp_lock_hint_nonspeculative) capture acquire + x = y + y = 2 * 10 + (8 - x) + !$omp end atomic + + +!CHECK: %[[constant_20:.*]] = arith.constant 20 : i32 +!CHECK: %[[constant_8:.*]] = arith.constant 8 : i32 +!CHECK: %[[temp:.*]] = fir.load %[[X]] : !fir.ref +!CHECK: %[[result:.*]] = arith.subi %[[constant_8]], %[[temp]] : i32 +!CHECK: %[[result_noreassoc:.*]] = fir.no_reassoc %[[result]] : i32 +!CHECK: %[[result:.*]] = arith.addi %[[constant_20]], %[[result_noreassoc]] : i32 +!CHECK: omp.atomic.capture { +!CHECK: omp.atomic.read %[[X]] = %[[Y]] : !fir.ref +!CHECK: omp.atomic.write %[[Y]] = %[[result]] : !fir.ref, i32 +!CHECK: } + + !$omp atomic capture + x = y + y = 2 * 10 + (8 - x) + !$omp end atomic +end program + + + +subroutine pointers_in_atomic_capture() + +!CHECK: %{{.*}} = fir.alloca !fir.box> {bindc_name = "a", uniq_name = "_QFpointers_in_atomic_captureEa"} +!CHECK: %[[A:.*]] = fir.alloca !fir.ptr {uniq_name = "_QFpointers_in_atomic_captureEa.addr"} +!CHECK: %[[temp:.*]] = fir.zero_bits !fir.ptr +!CHECK: fir.store %[[temp]] to %[[A]] : !fir.ref> +!CHECK: %{{.*}} = fir.alloca !fir.box> {bindc_name = "b", uniq_name = "_QFpointers_in_atomic_captureEb"} +!CHECK: %[[B:.*]] = fir.alloca !fir.ptr {uniq_name = "_QFpointers_in_atomic_captureEb.addr"} +!CHECK: %[[temp:.*]] = fir.zero_bits !fir.ptr +!CHECK: fir.store %[[temp]] to %[[B]] : !fir.ref> +!CHECK: %[[C:.*]] = fir.alloca i32 {bindc_name = "c", fir.target, uniq_name = "_QFpointers_in_atomic_captureEc"} +!CHECK: %[[D:.*]] = fir.alloca i32 {bindc_name = "d", fir.target, uniq_name = "_QFpointers_in_atomic_captureEd"} +!CHECK: %[[temp:.*]] = fir.convert %[[C]] : (!fir.ref) -> !fir.ptr +!CHECK: fir.store %[[temp]] to %[[A]] : !fir.ref> +!CHECK: %[[temp:.*]] = fir.convert %[[D]] : (!fir.ref) -> !fir.ptr +!CHECK: fir.store %[[temp]] to %[[B]] : !fir.ref> + integer, pointer :: a, b + integer, target :: c, d + a=>c + b=>d + +!CHECK: %[[loaded_A:.*]] = fir.load %[[A]] : !fir.ref> +!CHECK: %[[loaded_B:.*]] = fir.load %[[B]] : !fir.ref> +!CHECK: omp.atomic.capture { +!CHECK: omp.atomic.update %[[loaded_A]] : !fir.ptr { +!CHECK: ^bb0(%[[ARG:.*]]: i32): +!CHECK: %[[temp:.*]] = fir.load %[[B]] : !fir.ref> +!CHECK: %[[loaded_B_update_op:.*]] = fir.load %[[temp]] : !fir.ptr +!CHECK: %[[result:.*]] = arith.addi %[[ARG]], %[[loaded_B_update_op]] : i32 +!CHECK: omp.yield(%[[result]] : i32) +!CHECK: } +!CHECK: omp.atomic.read %[[loaded_B]] = %[[loaded_A]] : !fir.ptr +!CHECK: } + !$omp atomic capture + a = a + b + b = a + !$omp end atomic + +!CHECK: %[[loaded_B:.*]] = fir.load %[[B]] : !fir.ref> +!CHECK: %[[loaded_A:.*]] = fir.load %[[A]] : !fir.ref> +!CHECK: %[[const:.*]] = arith.constant 10 : i32 +!CHECK: %[[loaded_B_write_op:.*]] = fir.load %[[B]] : !fir.ref> +!CHECK: %[[temp:.*]] = fir.load %[[loaded_B_write_op]] : !fir.ptr +!CHECK: %[[evaluated_expr:.*]] = arith.addi %[[const]], %[[temp]] : i32 +!CHECK: omp.atomic.capture { +!CHECK: omp.atomic.read %[[loaded_B]] = %[[loaded_A]] : !fir.ptr +!CHECK: omp.atomic.write %[[loaded_A]] = %[[evaluated_expr]] : !fir.ptr, i32 +!CHECK: } +!CHECK: return +!CHECK: } + !$omp atomic capture + b = a + a = 10 + b + !$omp end atomic +end subroutine