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 @@ -1927,6 +1927,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, @@ -1965,20 +1995,68 @@ } } +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, + mlir::Type elementType) { + // 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, + mlir::TypeAttr::get(elementType), 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; @@ -1990,16 +2068,11 @@ genOmpAtomicHintAndMemoryOrderClauses(converter, *rightHandClauseList, hint, memoryOrder); auto atomicUpdateOp = firOpBuilder.create( - currentLocation, address, hint, memoryOrder); + currentLocation, lhs_addr, hint, memoryOrder); //// 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); @@ -2011,6 +2084,9 @@ assert(varDesignator && "Variable designator for atomic update assignment " "statement does not exist"); const auto *name = getDesignatorNameIfDataRef(varDesignator->value()); + if (!name) + TODO(converter.getCurrentLocation(), + "Array references as atomic update variable"); assert(name && name->symbol && "No symbol attached to atomic update variable"); converter.bindSymbol(*name->symbol, val); @@ -2019,10 +2095,11 @@ mlir::Block &block = atomicUpdateOp.getRegion().back(); firOpBuilder.setInsertionPointToEnd(&block); - mlir::Value result = fir::getBase(converter.genExprValue( + Fortran::lower::StatementContext stmtCtx; + mlir::Value rhs_expr = fir::getBase(converter.genExprValue( *Fortran::semantics::GetExpr(assignmentStmtExpr), stmtCtx)); mlir::Value convertResult = - firOpBuilder.createConvert(currentLocation, varType, result); + firOpBuilder.createConvert(currentLocation, varType, rhs_expr); // Insert the terminator: YieldOp. firOpBuilder.create(currentLocation, convertResult); // Reset the insert point to before the terminator. @@ -2033,8 +2110,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); @@ -2045,27 +2120,18 @@ const auto &assignmentStmtVariable = std::get( std::get<3>(atomicWrite.t).statement.t); Fortran::lower::StatementContext stmtCtx; - mlir::Value value = fir::getBase(converter.genExprValue( + // Get the value and address of atomic write operands. + 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 memoryOrder = nullptr; - genOmpAtomicHintAndMemoryOrderClauses(converter, leftHandClauseList, hint, - memoryOrder); - genOmpAtomicHintAndMemoryOrderClauses(converter, rightHandClauseList, hint, - memoryOrder); - firOpBuilder.create(currentLocation, address, value, - hint, memoryOrder); + 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); @@ -2084,17 +2150,9 @@ fir::getBase(converter.genExprAddr(fromExpr, stmtCtx)); mlir::Value toAddress = 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 memoryOrder = nullptr; - genOmpAtomicHintAndMemoryOrderClauses(converter, leftHandClauseList, hint, - memoryOrder); - genOmpAtomicHintAndMemoryOrderClauses(converter, rightHandClauseList, hint, - memoryOrder); - firOpBuilder.create( - currentLocation, fromAddress, toAddress, mlir::TypeAttr::get(elementType), - hint, memoryOrder); + genOmpAtomicCaptureStatement(converter, eval, fromAddress, toAddress, + &leftHandClauseList, &rightHandClauseList, + elementType); } static void @@ -2110,9 +2168,17 @@ const auto &assignmentStmtVariable = std::get( std::get<3>(atomicUpdate.t).statement.t); - genOmpAtomicUpdateStatement(converter, eval, assignmentStmtVariable, - assignmentStmtExpr, &leftHandClauseList, - &rightHandClauseList); + 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(); + genOmpAtomicUpdateStatement(converter, eval, lhs_addr, varType, + assignmentStmtVariable, assignmentStmtExpr, + &leftHandClauseList, &rightHandClauseList); } static void genOmpAtomic(Fortran::lower::AbstractConverter &converter, @@ -2128,10 +2194,137 @@ 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; + mlir::Type elementType; + // 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] + const Fortran::semantics::SomeExpr &fromExpr = + *Fortran::semantics::GetExpr(stmt1Expr); + elementType = converter.genType(fromExpr); + genOmpAtomicCaptureStatement(converter, eval, stmt1RHSArg, stmt1LHSArg, + /*leftHandClauseList=*/nullptr, + /*rightHandClauseList=*/nullptr, + elementType); + genOmpAtomicUpdateStatement(converter, eval, stmt1RHSArg, stmt2VarType, + stmt2Var, stmt2Expr, + /*leftHandClauseList=*/nullptr, + /*rightHandClauseList=*/nullptr); + } else { + // Atomic capture construct is of the form [capture-stmt, write-stmt] + const Fortran::semantics::SomeExpr &fromExpr = + *Fortran::semantics::GetExpr(stmt1Expr); + elementType = converter.genType(fromExpr); + genOmpAtomicCaptureStatement(converter, eval, stmt1RHSArg, stmt1LHSArg, + /*leftHandClauseList=*/nullptr, + /*rightHandClauseList=*/nullptr, + elementType); + genOmpAtomicWriteStatement(converter, eval, stmt1RHSArg, stmt2RHSArg, + /*leftHandClauseList=*/nullptr, + /*rightHandClauseList=*/nullptr); + } + } else { + // Atomic capture construct is of the form [update-stmt, capture-stmt] + firOpBuilder.setInsertionPointToEnd(&block); + const Fortran::semantics::SomeExpr &fromExpr = + *Fortran::semantics::GetExpr(stmt2Expr); + elementType = converter.genType(fromExpr); + genOmpAtomicCaptureStatement(converter, eval, stmt1LHSArg, stmt2LHSArg, + /*leftHandClauseList=*/nullptr, + /*rightHandClauseList=*/nullptr, elementType); + 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 @@ -2151,8 +2344,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,117 @@ +! 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: %[[A:.*]] = fir.alloca !fir.box> {bindc_name = "a", uniq_name = "_QFpointers_in_atomic_captureEa"} +!CHECK: {{.*}} = fir.zero_bits !fir.ptr +!CHECK: {{.*}} = fir.embox {{.*}} : (!fir.ptr) -> !fir.box> +!CHECK: fir.store {{.*}} to %[[A]] : !fir.ref>> +!CHECK: %[[B:.*]] = fir.alloca !fir.box> {bindc_name = "b", uniq_name = "_QFpointers_in_atomic_captureEb"} +!CHECK: {{.*}} = fir.zero_bits !fir.ptr +!CHECK: {{.*}} = fir.embox {{.*}} : (!fir.ptr) -> !fir.box> +!CHECK: fir.store {{.*}} 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: {{.*}} = fir.embox {{.*}} : (!fir.ref) -> !fir.box> +!CHECK: fir.store {{.*}} to %[[A]] : !fir.ref>> +!CHECK: {{.*}} = fir.embox {{.*}} : (!fir.ref) -> !fir.box> +!CHECK: fir.store {{.*}} to %[[B]] : !fir.ref>> +!CHECK: %[[loaded_A:.*]] = fir.load %[[A]] : !fir.ref>> +!CHECK: %[[loaded_A_addr:.*]] = fir.box_addr %[[loaded_A]] : (!fir.box>) -> !fir.ptr +!CHECK: %[[loaded_B:.*]] = fir.load %[[B]] : !fir.ref>> +!CHECK: %[[loaded_B_addr:.*]] = fir.box_addr %[[loaded_B]] : (!fir.box>) -> !fir.ptr +!CHECK: omp.atomic.capture { +!CHECK: omp.atomic.update %[[loaded_A_addr]] : !fir.ptr { +!CHECK: ^bb0(%[[ARG:.*]]: i32): +!CHECK: %[[PRIVATE_LOADED_B:.*]] = fir.load %[[B]] : !fir.ref>> +!CHECK: %[[PRIVATE_LOADED_B_addr:.*]] = fir.box_addr %[[PRIVATE_LOADED_B]] : (!fir.box>) -> !fir.ptr +!CHECK: %[[loaded_value:.*]] = fir.load %[[PRIVATE_LOADED_B_addr]] : !fir.ptr +!CHECK: %[[result:.*]] = arith.addi %[[ARG]], %[[loaded_value]] : i32 +!CHECK: omp.yield(%[[result]] : i32) +!CHECK: } +!CHECK: omp.atomic.read %[[loaded_B_addr]] = %[[loaded_A_addr]] : !fir.ptr, i32 +!CHECK: } + integer, pointer :: a, b + integer, target :: c, d + a=>c + b=>d + + !$omp atomic capture + a = a + b + b = a + !$omp end atomic +end subroutine