Skip to content

Commit 3ea6481

Browse files
committed
Added OpenMP 5.0 specification based lowering for atomic read and write constructs
1 parent 2233dbf commit 3ea6481

File tree

4 files changed

+300
-1
lines changed

4 files changed

+300
-1
lines changed

flang/lib/Lower/OpenMP.cpp

+132-1
Original file line numberDiff line numberDiff line change
@@ -714,6 +714,137 @@ static void genOMP(Fortran::lower::AbstractConverter &converter,
714714
&wsLoopOpClauseList, iv);
715715
}
716716

717+
static void genOmpAtomicHintAndMemoryOrderClauses(
718+
Fortran::lower::AbstractConverter &converter,
719+
const Fortran::parser::OmpAtomicClauseList &clauseList, uint64_t &hint,
720+
mlir::StringAttr &memory_order) {
721+
auto &firOpBuilder = converter.getFirOpBuilder();
722+
for (const auto &clause : clauseList.v) {
723+
if (auto ompClause = std::get_if<Fortran::parser::OmpClause>(&clause.u)) {
724+
if (auto hintClause =
725+
std::get_if<Fortran::parser::OmpClause::Hint>(&ompClause->u)) {
726+
const auto *expr = Fortran::semantics::GetExpr(hintClause->v);
727+
hint = *Fortran::evaluate::ToInt64(*expr);
728+
}
729+
} else if (auto ompMemoryOrderClause =
730+
std::get_if<Fortran::parser::OmpMemoryOrderClause>(
731+
&clause.u)) {
732+
if (std::get_if<Fortran::parser::OmpClause::Acquire>(
733+
&ompMemoryOrderClause->v.u)) {
734+
memory_order =
735+
firOpBuilder.getStringAttr(omp::stringifyClauseMemoryOrderKind(
736+
omp::ClauseMemoryOrderKind::acquire));
737+
} else if (std::get_if<Fortran::parser::OmpClause::Relaxed>(
738+
&ompMemoryOrderClause->v.u)) {
739+
memory_order =
740+
firOpBuilder.getStringAttr(omp::stringifyClauseMemoryOrderKind(
741+
omp::ClauseMemoryOrderKind::relaxed));
742+
} else if (std::get_if<Fortran::parser::OmpClause::SeqCst>(
743+
&ompMemoryOrderClause->v.u)) {
744+
memory_order =
745+
firOpBuilder.getStringAttr(omp::stringifyClauseMemoryOrderKind(
746+
omp::ClauseMemoryOrderKind::seq_cst));
747+
} else if (std::get_if<Fortran::parser::OmpClause::Release>(
748+
&ompMemoryOrderClause->v.u)) {
749+
memory_order =
750+
firOpBuilder.getStringAttr(omp::stringifyClauseMemoryOrderKind(
751+
omp::ClauseMemoryOrderKind::release));
752+
}
753+
}
754+
}
755+
}
756+
757+
static void
758+
genOmpAtomicWrite(Fortran::lower::AbstractConverter &converter,
759+
Fortran::lower::pft::Evaluation &eval,
760+
const Fortran::parser::OmpAtomicWrite &atomicWrite) {
761+
auto &firOpBuilder = converter.getFirOpBuilder();
762+
auto currentLocation = converter.getCurrentLocation();
763+
mlir::Value address;
764+
// If no hint clause is specified, the effect is as if
765+
// hint(omp_sync_hint_none) had been specified.
766+
uint64_t hint = 0;
767+
mlir::StringAttr memory_order = nullptr;
768+
const Fortran::parser::OmpAtomicClauseList &rightHandClauseList =
769+
std::get<2>(atomicWrite.t);
770+
const Fortran::parser::OmpAtomicClauseList &leftHandClauseList =
771+
std::get<0>(atomicWrite.t);
772+
const auto &assignmentStmtExpr =
773+
std::get<Fortran::parser::Expr>(std::get<3>(atomicWrite.t).statement.t);
774+
const auto &assignmentStmtVariable = std::get<Fortran::parser::Variable>(
775+
std::get<3>(atomicWrite.t).statement.t);
776+
Fortran::lower::StatementContext stmtCtx;
777+
auto value = fir::getBase(converter.genExprValue(
778+
*Fortran::semantics::GetExpr(assignmentStmtExpr), stmtCtx));
779+
if (auto varDesignator = std::get_if<
780+
Fortran::common::Indirection<Fortran::parser::Designator>>(
781+
&assignmentStmtVariable.u)) {
782+
if (const auto *name = getDesignatorNameIfDataRef(varDesignator->value())) {
783+
address = converter.getSymbolAddress(*name->symbol);
784+
}
785+
}
786+
787+
genOmpAtomicHintAndMemoryOrderClauses(converter, leftHandClauseList, hint,
788+
memory_order);
789+
genOmpAtomicHintAndMemoryOrderClauses(converter, rightHandClauseList, hint,
790+
memory_order);
791+
firOpBuilder.create<mlir::omp::AtomicWriteOp>(currentLocation, address, value,
792+
hint, memory_order);
793+
}
794+
795+
static void genOmpAtomicRead(Fortran::lower::AbstractConverter &converter,
796+
Fortran::lower::pft::Evaluation &eval,
797+
const Fortran::parser::OmpAtomicRead &atomicRead) {
798+
auto &firOpBuilder = converter.getFirOpBuilder();
799+
auto currentLocation = converter.getCurrentLocation();
800+
mlir::Type resultType;
801+
mlir::Value address;
802+
// If no hint clause is specified, the effect is as if
803+
// hint(omp_sync_hint_none) had been specified.
804+
uint64_t hint = 0;
805+
mlir::StringAttr memory_order = nullptr;
806+
const Fortran::parser::OmpAtomicClauseList &rightHandClauseList =
807+
std::get<2>(atomicRead.t);
808+
const Fortran::parser::OmpAtomicClauseList &leftHandClauseList =
809+
std::get<0>(atomicRead.t);
810+
const auto &assignmentStmtExpr =
811+
std::get<Fortran::parser::Expr>(std::get<3>(atomicRead.t).statement.t);
812+
if (auto exprDesignator = std::get_if<
813+
Fortran::common::Indirection<Fortran::parser::Designator>>(
814+
&assignmentStmtExpr.u)) {
815+
if (const auto *name =
816+
getDesignatorNameIfDataRef(exprDesignator->value())) {
817+
address = converter.getSymbolAddress(*name->symbol);
818+
resultType = converter.genType(*name->symbol);
819+
}
820+
}
821+
genOmpAtomicHintAndMemoryOrderClauses(converter, leftHandClauseList, hint,
822+
memory_order);
823+
genOmpAtomicHintAndMemoryOrderClauses(converter, rightHandClauseList, hint,
824+
memory_order);
825+
firOpBuilder.create<mlir::omp::AtomicReadOp>(currentLocation, resultType,
826+
address, hint, memory_order);
827+
}
828+
829+
static void
830+
genOMP(Fortran::lower::AbstractConverter &converter,
831+
Fortran::lower::pft::Evaluation &eval,
832+
const Fortran::parser::OpenMPAtomicConstruct &atomicConstruct) {
833+
std::visit(Fortran::common::visitors{
834+
[&](const Fortran::parser::OmpAtomicRead &atomicRead) {
835+
genOmpAtomicRead(converter, eval, atomicRead);
836+
},
837+
[&](const Fortran::parser::OmpAtomicWrite &atomicWrite) {
838+
genOmpAtomicWrite(converter, eval, atomicWrite);
839+
},
840+
[&](const auto &) {
841+
TODO(converter.getCurrentLocation(),
842+
"Atomic update & capture");
843+
},
844+
},
845+
atomicConstruct.u);
846+
}
847+
717848
static void
718849
genOMP(Fortran::lower::AbstractConverter &converter,
719850
Fortran::lower::pft::Evaluation &eval,
@@ -846,7 +977,7 @@ void Fortran::lower::genOpenMPConstruct(
846977
genOMP(converter, eval, blockConstruct);
847978
},
848979
[&](const Fortran::parser::OpenMPAtomicConstruct &atomicConstruct) {
849-
TODO(converter.getCurrentLocation(), "OpenMPAtomicConstruct");
980+
genOMP(converter, eval, atomicConstruct);
850981
},
851982
[&](const Fortran::parser::OpenMPCriticalConstruct
852983
&criticalConstruct) {

flang/lib/Optimizer/Dialect/FIRType.cpp

+32
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
#include "flang/Optimizer/Dialect/FIRType.h"
1414
#include "flang/Optimizer/Dialect/FIRDialect.h"
15+
#include "mlir/Dialect/OpenMP/OpenMPDialect.h"
1516
#include "mlir/IR/Builders.h"
1617
#include "mlir/IR/BuiltinDialect.h"
1718
#include "mlir/IR/Diagnostics.h"
@@ -920,11 +921,42 @@ bool fir::isCharacterProcedureTuple(mlir::Type ty, bool acceptRawFunc) {
920921
//===----------------------------------------------------------------------===//
921922
// FIROpsDialect
922923
//===----------------------------------------------------------------------===//
924+
namespace {
925+
/// Model for FIR pointer like types that already provide a `getElementType`
926+
/// method
927+
template <typename T>
928+
struct PointerLikeModel
929+
: public mlir::omp::PointerLikeType::ExternalModel<PointerLikeModel<T>, T> {
930+
mlir::Type getElementType(mlir::Type pointer) const {
931+
return pointer.cast<T>().getElementType();
932+
}
933+
};
923934

935+
template <typename T>
936+
struct AlternativePointerLikeModel
937+
: public mlir::omp::PointerLikeType::ExternalModel<
938+
AlternativePointerLikeModel<T>, T> {
939+
mlir::Type getElementType(mlir::Type pointer) const {
940+
return pointer.cast<T>().getEleTy();
941+
}
942+
};
943+
944+
} // end namespace
924945
void FIROpsDialect::registerTypes() {
925946
addTypes<BoxType, BoxCharType, BoxProcType, CharacterType, fir::ComplexType,
926947
FieldType, HeapType, fir::IntegerType, LenType, LogicalType,
927948
LLVMPointerType, PointerType, RealType, RecordType, ReferenceType,
928949
SequenceType, ShapeType, ShapeShiftType, ShiftType, SliceType,
929950
TypeDescType, fir::VectorType>();
951+
fir::ReferenceType::attachInterface<PointerLikeModel<fir::ReferenceType>>(
952+
*getContext());
953+
954+
fir::PointerType::attachInterface<PointerLikeModel<fir::PointerType>>(
955+
*getContext());
956+
957+
fir::HeapType::attachInterface<AlternativePointerLikeModel<fir::HeapType>>(
958+
*getContext());
959+
960+
fir::LLVMPointerType::attachInterface<
961+
AlternativePointerLikeModel<fir::LLVMPointerType>>(*getContext());
930962
}

flang/test/Lower/OpenMP/atomic01.f90

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
! RUN: bbc -fopenmp -emit-fir %s -o - | \
2+
! RUN: FileCheck %s --check-prefix=FIRDialect
3+
! RUN: bbc -fopenmp %s -o - | \
4+
! RUN: tco --disable-llvm --print-ir-after=fir-to-llvm-ir 2>&1 | \
5+
! RUN: FileCheck %s --check-prefix=LLVMIRDialect
6+
7+
! This test checks the lowering of atomic read
8+
9+
!FIRDialect: func @_QQmain() {
10+
!FIRDialect: %[[VAR_B:.*]] = fir.address_of(@_QFEb) : !fir.ref<!fir.char<1>>
11+
!FIRDialect: %[[VAR_C:.*]] = fir.alloca !fir.logical<4> {bindc_name = "c", uniq_name = "_QFEc"}
12+
!FIRDialect: %[[VAR_D:.*]] = fir.alloca !fir.logical<4> {bindc_name = "d", uniq_name = "_QFEd"}
13+
!FIRDialect: %[[VAR_F:.*]] = fir.address_of(@_QFEf) : !fir.ref<!fir.char<1,8>>
14+
!FIRDialect: %[[VAR_G:.*]] = fir.alloca f32 {bindc_name = "g", uniq_name = "_QFEg"}
15+
!FIRDialect: %[[VAR_H:.*]] = fir.alloca f32 {bindc_name = "h", uniq_name = "_QFEh"}
16+
!FIRDialect: %[[VAR_X:.*]] = fir.alloca i32 {bindc_name = "x", uniq_name = "_QFEx"}
17+
!FIRDialect: %[[VAR_Y:.*]] = fir.alloca i32 {bindc_name = "y", uniq_name = "_QFEy"}
18+
!FIRDialect: {{.*}} = omp.atomic.read %[[VAR_Y]] memory_order(acquire) hint(uncontended) : !fir.ref<i32> -> i32
19+
!FIRDialect: {{.*}} = omp.atomic.read %[[VAR_B]] memory_order(relaxed) : !fir.ref<!fir.char<1>> -> !fir.char<1>
20+
!FIRDialect: {{.*}} = omp.atomic.read %[[VAR_D]] memory_order(seq_cst) hint(contended) : !fir.ref<!fir.logical<4>> -> !fir.logical<4>
21+
!FIRDialect: {{.*}} = omp.atomic.read %[[VAR_F]] hint(speculative) : !fir.ref<!fir.char<1,8>> -> !fir.char<1,8>
22+
!FIRDialect: {{.*}} = omp.atomic.read %[[VAR_H]] hint(nonspeculative) : !fir.ref<f32> -> f32
23+
!FIRDialect: {{.*}} = omp.atomic.read %[[VAR_X]] : !fir.ref<i32> -> i32
24+
!FIRDialect: return
25+
!FIRDialect: }
26+
27+
!LLVMIRDialect: llvm.func @_QQmain() {
28+
!LLVMIRDialect: %[[LLVM_VAR_B:.*]] = llvm.mlir.addressof @_QFEb : !llvm.ptr<array<1 x i8>>
29+
!LLVMIRDialect: {{.*}} = llvm.mlir.constant(1 : i64) : i64
30+
!LLVIRDialect: %[[LLVM_VAR_C:.*]] = llvm.alloca {{.*}} x i32 {bindc_name = "c", in_type = !fir.logical<4>, operand_segment_sizes = dense<0> : vector<2xi32>, uniq_name = "_QFEc"} : (i64) -> !llvm.ptr<i32>
31+
!LLVMIRDialect: {{.*}} = llvm.mlir.constant(1 : i64) : i64
32+
!LLVMIRDialect: %[[LLVM_VAR_D:.*]] = llvm.alloca {{.*}} x i32 {bindc_name = "d", in_type = !fir.logical<4>, operand_segment_sizes = dense<0> : vector<2xi32>, uniq_name = "_QFEd"} : (i64) -> !llvm.ptr<i32>
33+
!LLVMIRDialect: %[[LLVM_VAR_F:.*]] = llvm.mlir.addressof @_QFEf : !llvm.ptr<array<8 x i8>>
34+
!LLVMIRDialect: {{.*}} = llvm.mlir.constant(1 : i64) : i64
35+
!LLVMIRDialect: %[[LLVM_VAR_G:.*]] = llvm.alloca {{.*}} x f32 {bindc_name = "g", in_type = f32, operand_segment_sizes = dense<0> : vector<2xi32>, uniq_name = "_QFEg"} : (i64) -> !llvm.ptr<f32>
36+
!LLVMIRDialect: {{.*}} = llvm.mlir.constant(1 : i64) : i64
37+
!LLVMIRDialect: %[[LLVM_VAR_H:.*]] = llvm.alloca {{.*}} x f32 {bindc_name = "h", in_type = f32, operand_segment_sizes = dense<0> : vector<2xi32>, uniq_name = "_QFEh"} : (i64) -> !llvm.ptr<f32>
38+
!LLVMIRDialect: {{.*}} = llvm.mlir.constant(1 : i64) : i64
39+
!LLVMIRDialect: %[[LLVM_VAR_X:.*]] = llvm.alloca {{.*}} x i32 {bindc_name = "x", in_type = i32, operand_segment_sizes = dense<0> : vector<2xi32>, uniq_name = "_QFEx"} : (i64) -> !llvm.ptr<i32>
40+
!LLVMIRDialect: {{.*}} = llvm.mlir.constant(1 : i64) : i64
41+
!LLVMIRDialect: %[[LLVM_VAR_Y:.*]] = llvm.alloca {{.*}} x i32 {bindc_name = "y", in_type = i32, operand_segment_sizes = dense<0> : vector<2xi32>, uniq_name = "_QFEy"} : (i64) -> !llvm.ptr<i32>
42+
!LLVMIRDialect: {{.*}} = omp.atomic.read %[[LLVM_VAR_Y]] memory_order(acquire) hint(uncontended) : !llvm.ptr<i32> -> i32
43+
!LLVMIRDialect: {{.*}} = omp.atomic.read %[[LLVM_VAR_B]] memory_order(relaxed) : !llvm.ptr<array<1 x i8>> -> !fir.char<1>
44+
!LLVMIRDialect: {{.*}} = omp.atomic.read %[[LLVM_VAR_D]] memory_order(seq_cst) hint(contended) : !llvm.ptr<i32> -> !fir.logical<4>
45+
!LLVMIRDialect: {{.*}} = omp.atomic.read %[[LLVM_VAR_F]] hint(speculative) : !llvm.ptr<array<8 x i8>> -> !fir.char<1,8>
46+
!LLVMIRDialect: {{.*}} = omp.atomic.read %[[LLVM_VAR_H]] hint(nonspeculative) : !llvm.ptr<f32> -> f32
47+
!LLVMIRDialect: {{.*}} = omp.atomic.read %[[LLVM_VAR_X]] : !llvm.ptr<i32> -> i32
48+
!LLVMIRDialect: llvm.return
49+
!LLVMIRDialect: }
50+
51+
program OmpAtomic
52+
53+
use omp_lib
54+
integer :: x, y
55+
character :: a, b
56+
logical :: c, d
57+
character(8) :: e, f
58+
real g, h
59+
!$omp atomic acquire read hint(omp_sync_hint_uncontended)
60+
x = y
61+
!$omp atomic relaxed read hint(omp_sync_hint_none)
62+
a = b
63+
!$omp atomic read seq_cst hint(omp_sync_hint_contended)
64+
c = d
65+
!$omp atomic read hint(omp_sync_hint_speculative)
66+
e = f
67+
!$omp atomic read hint(omp_sync_hint_nonspeculative)
68+
g = h
69+
!$omp atomic read
70+
g = x
71+
end program OmpAtomic

flang/test/Lower/OpenMP/atomic02.f90

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
! RUN: bbc -fopenmp -emit-fir %s -o - | \
2+
! RUN: FileCheck %s --check-prefix=FIRDialect
3+
! RUN: bbc -fopenmp %s -o - | \
4+
! RUN: tco --disable-llvm --print-ir-after=fir-to-llvm-ir 2>&1 | \
5+
! RUN: FileCheck %s --check-prefix=LLVMIRDialect
6+
7+
! This test checks the lowering of atomic write
8+
9+
!FIRDialect: func @_QQmain() {
10+
!FIRDialect: %[[VAR_X:.*]] = fir.alloca i32 {bindc_name = "x", uniq_name = "_QFEx"}
11+
!FIRDialect: %[[VAR_Y:.*]] = fir.alloca i32 {bindc_name = "y", uniq_name = "_QFEy"}
12+
!FIRDialect: %[[VAR_Z:.*]] = fir.alloca i32 {bindc_name = "z", uniq_name = "_QFEz"}
13+
!FIRDialect: %[[CONST_44:.*]] = arith.constant 44 : i32
14+
!FIRDialect: omp.atomic.write %[[VAR_X]], %[[CONST_44]] memory_order(seq_cst) hint(uncontended) : !fir.ref<i32>, i32
15+
!FIRDialect: %[[CONST_7:.*]] = arith.constant 7 : i32
16+
!FIRDialect: {{.*}} = fir.load %[[VAR_Y]] : !fir.ref<i32>
17+
!FIRDialect: %[[VAR_7y:.*]] = arith.muli %c7_i32, %3 : i32
18+
!FIRDialect: omp.atomic.write %[[VAR_X]], %[[VAR_7y]] memory_order(relaxed) : !fir.ref<i32>, i32
19+
!FIRDialect: %[[CONST_10:.*]] = arith.constant 10 : i32
20+
!FIRDialect: {{.*}} = fir.load %[[VAR_X]] : !fir.ref<i32>
21+
!FIRDialect: {{.*}} = arith.muli %[[CONST_10]], {{.*}} : i32
22+
!FIRDialect: {{.*}} = fir.load %[[VAR_Z]] : !fir.ref<i32>
23+
!FIRDialect: %[[CONST_2:.*]] = arith.constant 2 : i32
24+
!FIRDialect: {{.*}} = arith.divsi {{.*}}, %[[CONST_2]] : i32
25+
!FIRDialect: {{.*}} = arith.addi {{.*}}, {{.*}} : i32
26+
!FIRDialect: omp.atomic.write %[[VAR_Y]], {{.*}} memory_order(release) hint(speculative) : !fir.ref<i32>, i32
27+
!FIRDialect: return
28+
!FIRDialect: }
29+
30+
!LLVMIRDialect: llvm.func @_QQmain() {
31+
!LLVMIRDialect: %[[LLVM_VAR_44:.*]] = llvm.mlir.constant(44 : i32) : i32
32+
!LLVMIRDialect: %[[LLVM_VAR_7:.*]] = llvm.mlir.constant(7 : i32) : i32
33+
!LLVMIRDialect: %[[LLVM_VAR_10:.*]] = llvm.mlir.constant(10 : i32) : i32
34+
!LLVMIRDialect: %[[LLVM_VAR_2:.*]] = llvm.mlir.constant(2 : i32) : i32
35+
!LLVMIRDialect: %[[LLVM_VAR_1:.*]] = llvm.mlir.constant(1 : i64) : i64
36+
!LLVMIRDialect: %[[LLVM_VAR_X:.*]] = llvm.alloca {{.*}} x i32 {bindc_name = "x", in_type = i32, operand_segment_sizes = dense<0> : vector<2xi32>, uniq_name = "_QFEx"} : (i64) -> !llvm.ptr<i32>
37+
!LLVMIRDialect: %[[LLVM_VAR_1_SECOND:.*]] = llvm.mlir.constant(1 : i64) : i64
38+
!LLVMIRDialect: %[[LLVM_VAR_Y:.*]] = llvm.alloca {{.*}} x i32 {bindc_name = "y", in_type = i32, operand_segment_sizes = dense<0> : vector<2xi32>, uniq_name = "_QFEy"} : (i64) -> !llvm.ptr<i32>
39+
!LLVMIRDialect: %[[LLVM_VAR_1_THIRD:.*]] = llvm.mlir.constant(1 : i64) : i64
40+
!LLVMIRDialect: %[[LLVM_VAR_Z:.*]] = llvm.alloca {{.*}} x i32 {bindc_name = "z", in_type = i32, operand_segment_sizes = dense<0> : vector<2xi32>, uniq_name = "_QFEz"} : (i64) -> !llvm.ptr<i32>
41+
!LLVMIRDialect: omp.atomic.write %[[LLVM_VAR_X]], %[[LLVM_VAR_44]] memory_order(seq_cst) hint(uncontended) : !llvm.ptr<i32>, i32
42+
!LLVMIRDialect: {{.*}} = llvm.load %[[LLVM_VAR_Y]] : !llvm.ptr<i32>
43+
!LLVMIRDialect: %[[LLVM_VAR_MUL_RESULT:.*]] = llvm.mul {{.*}}, %[[LLVM_VAR_7]] : i32
44+
!LLVMIRDialect: omp.atomic.write %[[LLVM_VAR_X]], %[[LLVM_VAR_MUL_RESULT]] memory_order(relaxed) : !llvm.ptr<i32>, i32
45+
!LLVMIRDialect: {{.*}} = llvm.load %[[LLVM_VAR_X]] : !llvm.ptr<i32>
46+
!LLVMIRDialect: {{.*}} = llvm.mul {{.*}}, %[[LLVM_VAR_10]] : i32
47+
!LLVMIRDialect: {{.*}} = llvm.load %[[LLVM_VAR_Z]] : !llvm.ptr<i32>
48+
!LLVMIRDialect: {{.*}} = llvm.sdiv {{.*}}, %[[LLVM_VAR_2]] : i32
49+
!LLVMIRDialect: {{.*}} = llvm.add {{.*}} : i32
50+
!LLVMIRDialect: omp.atomic.write %[[LLVM_VAR_Y]], {{.*}} memory_order(release) hint(speculative) : !llvm.ptr<i32>, i32
51+
!LLVMIRDialect: llvm.return
52+
!LLVMIRDialect: }
53+
54+
program OmpAtomicWrite
55+
use omp_lib
56+
integer :: x, y, z
57+
!$omp atomic seq_cst write hint(omp_sync_hint_uncontended)
58+
x = 8*4 + 12
59+
60+
!$omp atomic write relaxed
61+
x = 7 * y
62+
63+
!$omp atomic write release hint(omp_sync_hint_speculative)
64+
y = 10*x + z/2
65+
end program OmpAtomicWrite

0 commit comments

Comments
 (0)