Skip to content
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

Added OpenMP 5.0 specification based lowering of atomic read and write #1367

Open
wants to merge 1 commit into
base: fir-dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
133 changes: 132 additions & 1 deletion flang/lib/Lower/OpenMP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -714,6 +714,137 @@ static void genOMP(Fortran::lower::AbstractConverter &converter,
&wsLoopOpClauseList, iv);
}

static void genOmpAtomicHintAndMemoryOrderClauses(
Fortran::lower::AbstractConverter &converter,
const Fortran::parser::OmpAtomicClauseList &clauseList, uint64_t &hint,
mlir::StringAttr &memory_order) {
auto &firOpBuilder = converter.getFirOpBuilder();
for (const auto &clause : clauseList.v) {
if (auto ompClause = std::get_if<Fortran::parser::OmpClause>(&clause.u)) {
NimishMishra marked this conversation as resolved.
Show resolved Hide resolved
if (auto hintClause =
std::get_if<Fortran::parser::OmpClause::Hint>(&ompClause->u)) {
const auto *expr = Fortran::semantics::GetExpr(hintClause->v);
hint = *Fortran::evaluate::ToInt64(*expr);
}
} else if (auto ompMemoryOrderClause =
std::get_if<Fortran::parser::OmpMemoryOrderClause>(
&clause.u)) {
if (std::get_if<Fortran::parser::OmpClause::Acquire>(
&ompMemoryOrderClause->v.u)) {
memory_order =
firOpBuilder.getStringAttr(omp::stringifyClauseMemoryOrderKind(
omp::ClauseMemoryOrderKind::acquire));
} else if (std::get_if<Fortran::parser::OmpClause::Relaxed>(
&ompMemoryOrderClause->v.u)) {
memory_order =
firOpBuilder.getStringAttr(omp::stringifyClauseMemoryOrderKind(
omp::ClauseMemoryOrderKind::relaxed));
} else if (std::get_if<Fortran::parser::OmpClause::SeqCst>(
&ompMemoryOrderClause->v.u)) {
memory_order =
firOpBuilder.getStringAttr(omp::stringifyClauseMemoryOrderKind(
omp::ClauseMemoryOrderKind::seq_cst));
} else if (std::get_if<Fortran::parser::OmpClause::Release>(
&ompMemoryOrderClause->v.u)) {
memory_order =
firOpBuilder.getStringAttr(omp::stringifyClauseMemoryOrderKind(
omp::ClauseMemoryOrderKind::release));
}
}
}
}

static void
genOmpAtomicWrite(Fortran::lower::AbstractConverter &converter,
Fortran::lower::pft::Evaluation &eval,
const Fortran::parser::OmpAtomicWrite &atomicWrite) {
auto &firOpBuilder = converter.getFirOpBuilder();
auto currentLocation = converter.getCurrentLocation();
mlir::Value address;
// If no hint clause is specified, the effect is as if
// hint(omp_sync_hint_none) had been specified.
uint64_t hint = 0;
mlir::StringAttr memory_order = nullptr;
const Fortran::parser::OmpAtomicClauseList &rightHandClauseList =
std::get<2>(atomicWrite.t);
const Fortran::parser::OmpAtomicClauseList &leftHandClauseList =
std::get<0>(atomicWrite.t);
const auto &assignmentStmtExpr =
std::get<Fortran::parser::Expr>(std::get<3>(atomicWrite.t).statement.t);
const auto &assignmentStmtVariable = std::get<Fortran::parser::Variable>(
std::get<3>(atomicWrite.t).statement.t);
Fortran::lower::StatementContext stmtCtx;
auto value = fir::getBase(converter.genExprValue(
*Fortran::semantics::GetExpr(assignmentStmtExpr), stmtCtx));
if (auto varDesignator = std::get_if<
Fortran::common::Indirection<Fortran::parser::Designator>>(
&assignmentStmtVariable.u)) {
if (const auto *name = getDesignatorNameIfDataRef(varDesignator->value())) {
address = converter.getSymbolAddress(*name->symbol);
}
}

genOmpAtomicHintAndMemoryOrderClauses(converter, leftHandClauseList, hint,
memory_order);
genOmpAtomicHintAndMemoryOrderClauses(converter, rightHandClauseList, hint,
memory_order);
firOpBuilder.create<mlir::omp::AtomicWriteOp>(currentLocation, address, value,
hint, memory_order);
}

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();
mlir::Type resultType;
mlir::Value address;
// If no hint clause is specified, the effect is as if
// hint(omp_sync_hint_none) had been specified.
uint64_t hint = 0;
mlir::StringAttr memory_order = nullptr;
const Fortran::parser::OmpAtomicClauseList &rightHandClauseList =
std::get<2>(atomicRead.t);
const Fortran::parser::OmpAtomicClauseList &leftHandClauseList =
std::get<0>(atomicRead.t);
const auto &assignmentStmtExpr =
std::get<Fortran::parser::Expr>(std::get<3>(atomicRead.t).statement.t);
if (auto exprDesignator = std::get_if<
Fortran::common::Indirection<Fortran::parser::Designator>>(
&assignmentStmtExpr.u)) {
if (const auto *name =
getDesignatorNameIfDataRef(exprDesignator->value())) {
address = converter.getSymbolAddress(*name->symbol);
resultType = converter.genType(*name->symbol);
}
}
genOmpAtomicHintAndMemoryOrderClauses(converter, leftHandClauseList, hint,
memory_order);
genOmpAtomicHintAndMemoryOrderClauses(converter, rightHandClauseList, hint,
memory_order);
firOpBuilder.create<mlir::omp::AtomicReadOp>(currentLocation, resultType,
address, hint, memory_order);
}

static void
genOMP(Fortran::lower::AbstractConverter &converter,
Fortran::lower::pft::Evaluation &eval,
const Fortran::parser::OpenMPAtomicConstruct &atomicConstruct) {
std::visit(Fortran::common::visitors{
[&](const Fortran::parser::OmpAtomicRead &atomicRead) {
genOmpAtomicRead(converter, eval, atomicRead);
},
[&](const Fortran::parser::OmpAtomicWrite &atomicWrite) {
genOmpAtomicWrite(converter, eval, atomicWrite);
},
[&](const auto &) {
TODO(converter.getCurrentLocation(),
"Atomic update & capture");
},
},
atomicConstruct.u);
}

static void
genOMP(Fortran::lower::AbstractConverter &converter,
Fortran::lower::pft::Evaluation &eval,
Expand Down Expand Up @@ -846,7 +977,7 @@ void Fortran::lower::genOpenMPConstruct(
genOMP(converter, eval, blockConstruct);
},
[&](const Fortran::parser::OpenMPAtomicConstruct &atomicConstruct) {
TODO(converter.getCurrentLocation(), "OpenMPAtomicConstruct");
genOMP(converter, eval, atomicConstruct);
},
[&](const Fortran::parser::OpenMPCriticalConstruct
&criticalConstruct) {
Expand Down
32 changes: 32 additions & 0 deletions flang/lib/Optimizer/Dialect/FIRType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

#include "flang/Optimizer/Dialect/FIRType.h"
#include "flang/Optimizer/Dialect/FIRDialect.h"
#include "mlir/Dialect/OpenMP/OpenMPDialect.h"
#include "mlir/IR/Builders.h"
#include "mlir/IR/BuiltinDialect.h"
#include "mlir/IR/Diagnostics.h"
Expand Down Expand Up @@ -920,11 +921,42 @@ bool fir::isCharacterProcedureTuple(mlir::Type ty, bool acceptRawFunc) {
//===----------------------------------------------------------------------===//
// FIROpsDialect
//===----------------------------------------------------------------------===//
namespace {
/// Model for FIR pointer like types that already provide a `getElementType`
/// method
template <typename T>
struct PointerLikeModel
: public mlir::omp::PointerLikeType::ExternalModel<PointerLikeModel<T>, T> {
mlir::Type getElementType(mlir::Type pointer) const {
return pointer.cast<T>().getElementType();
}
};

template <typename T>
struct AlternativePointerLikeModel
: public mlir::omp::PointerLikeType::ExternalModel<
AlternativePointerLikeModel<T>, T> {
mlir::Type getElementType(mlir::Type pointer) const {
return pointer.cast<T>().getEleTy();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once upstreaming is complete we should check with @schweitzpgi whether it is OK for all these types to have the same getElementType function.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kiranchandramohan There's a little hunch with the upstreaming. I will ask on slack. Meanwhile, I addressed all the nits and pushed an update here. Upstreaming may take a while I guess

}
};

} // end namespace
void FIROpsDialect::registerTypes() {
addTypes<BoxType, BoxCharType, BoxProcType, CharacterType, fir::ComplexType,
FieldType, HeapType, fir::IntegerType, LenType, LogicalType,
LLVMPointerType, PointerType, RealType, RecordType, ReferenceType,
SequenceType, ShapeType, ShapeShiftType, ShiftType, SliceType,
TypeDescType, fir::VectorType>();
fir::ReferenceType::attachInterface<PointerLikeModel<fir::ReferenceType>>(
NimishMishra marked this conversation as resolved.
Show resolved Hide resolved
*getContext());

fir::PointerType::attachInterface<PointerLikeModel<fir::PointerType>>(
*getContext());

fir::HeapType::attachInterface<AlternativePointerLikeModel<fir::HeapType>>(
*getContext());

fir::LLVMPointerType::attachInterface<
AlternativePointerLikeModel<fir::LLVMPointerType>>(*getContext());
}
71 changes: 71 additions & 0 deletions flang/test/Lower/OpenMP/atomic01.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
! RUN: bbc -fopenmp -emit-fir %s -o - | \
! RUN: FileCheck %s --check-prefix=FIRDialect
! RUN: bbc -fopenmp %s -o - | \
! RUN: tco --disable-llvm --print-ir-after=fir-to-llvm-ir 2>&1 | \
! RUN: FileCheck %s --check-prefix=LLVMIRDialect
NimishMishra marked this conversation as resolved.
Show resolved Hide resolved

! This test checks the lowering of atomic read

!FIRDialect: func @_QQmain() {
!FIRDialect: %[[VAR_B:.*]] = fir.address_of(@_QFEb) : !fir.ref<!fir.char<1>>
!FIRDialect: %[[VAR_C:.*]] = fir.alloca !fir.logical<4> {bindc_name = "c", uniq_name = "_QFEc"}
!FIRDialect: %[[VAR_D:.*]] = fir.alloca !fir.logical<4> {bindc_name = "d", uniq_name = "_QFEd"}
!FIRDialect: %[[VAR_F:.*]] = fir.address_of(@_QFEf) : !fir.ref<!fir.char<1,8>>
!FIRDialect: %[[VAR_G:.*]] = fir.alloca f32 {bindc_name = "g", uniq_name = "_QFEg"}
!FIRDialect: %[[VAR_H:.*]] = fir.alloca f32 {bindc_name = "h", uniq_name = "_QFEh"}
!FIRDialect: %[[VAR_X:.*]] = fir.alloca i32 {bindc_name = "x", uniq_name = "_QFEx"}
!FIRDialect: %[[VAR_Y:.*]] = fir.alloca i32 {bindc_name = "y", uniq_name = "_QFEy"}
!FIRDialect: {{.*}} = omp.atomic.read %[[VAR_Y]] memory_order(acquire) hint(uncontended) : !fir.ref<i32> -> i32
!FIRDialect: {{.*}} = omp.atomic.read %[[VAR_B]] memory_order(relaxed) : !fir.ref<!fir.char<1>> -> !fir.char<1>
!FIRDialect: {{.*}} = omp.atomic.read %[[VAR_D]] memory_order(seq_cst) hint(contended) : !fir.ref<!fir.logical<4>> -> !fir.logical<4>
!FIRDialect: {{.*}} = omp.atomic.read %[[VAR_F]] hint(speculative) : !fir.ref<!fir.char<1,8>> -> !fir.char<1,8>
!FIRDialect: {{.*}} = omp.atomic.read %[[VAR_H]] hint(nonspeculative) : !fir.ref<f32> -> f32
!FIRDialect: {{.*}} = omp.atomic.read %[[VAR_X]] : !fir.ref<i32> -> i32
!FIRDialect: return
!FIRDialect: }

!LLVMIRDialect: llvm.func @_QQmain() {
!LLVMIRDialect: %[[LLVM_VAR_B:.*]] = llvm.mlir.addressof @_QFEb : !llvm.ptr<array<1 x i8>>
!LLVMIRDialect: {{.*}} = llvm.mlir.constant(1 : i64) : i64
!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>
!LLVMIRDialect: {{.*}} = llvm.mlir.constant(1 : i64) : i64
!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>
!LLVMIRDialect: %[[LLVM_VAR_F:.*]] = llvm.mlir.addressof @_QFEf : !llvm.ptr<array<8 x i8>>
!LLVMIRDialect: {{.*}} = llvm.mlir.constant(1 : i64) : i64
!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>
!LLVMIRDialect: {{.*}} = llvm.mlir.constant(1 : i64) : i64
!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>
!LLVMIRDialect: {{.*}} = llvm.mlir.constant(1 : i64) : i64
!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>
!LLVMIRDialect: {{.*}} = llvm.mlir.constant(1 : i64) : i64
!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>
!LLVMIRDialect: {{.*}} = omp.atomic.read %[[LLVM_VAR_Y]] memory_order(acquire) hint(uncontended) : !llvm.ptr<i32> -> i32
!LLVMIRDialect: {{.*}} = omp.atomic.read %[[LLVM_VAR_B]] memory_order(relaxed) : !llvm.ptr<array<1 x i8>> -> !fir.char<1>
!LLVMIRDialect: {{.*}} = omp.atomic.read %[[LLVM_VAR_D]] memory_order(seq_cst) hint(contended) : !llvm.ptr<i32> -> !fir.logical<4>
!LLVMIRDialect: {{.*}} = omp.atomic.read %[[LLVM_VAR_F]] hint(speculative) : !llvm.ptr<array<8 x i8>> -> !fir.char<1,8>
!LLVMIRDialect: {{.*}} = omp.atomic.read %[[LLVM_VAR_H]] hint(nonspeculative) : !llvm.ptr<f32> -> f32
!LLVMIRDialect: {{.*}} = omp.atomic.read %[[LLVM_VAR_X]] : !llvm.ptr<i32> -> i32
!LLVMIRDialect: llvm.return
!LLVMIRDialect: }

program OmpAtomic

use omp_lib
integer :: x, y
character :: a, b
logical :: c, d
character(8) :: e, f
real g, h
!$omp atomic acquire read hint(omp_sync_hint_uncontended)
x = y
!$omp atomic relaxed read hint(omp_sync_hint_none)
a = b
!$omp atomic read seq_cst hint(omp_sync_hint_contended)
c = d
!$omp atomic read hint(omp_sync_hint_speculative)
e = f
!$omp atomic read hint(omp_sync_hint_nonspeculative)
g = h
!$omp atomic read
g = x
end program OmpAtomic
65 changes: 65 additions & 0 deletions flang/test/Lower/OpenMP/atomic02.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
! RUN: bbc -fopenmp -emit-fir %s -o - | \
! RUN: FileCheck %s --check-prefix=FIRDialect
! RUN: bbc -fopenmp %s -o - | \
! RUN: tco --disable-llvm --print-ir-after=fir-to-llvm-ir 2>&1 | \
! RUN: FileCheck %s --check-prefix=LLVMIRDialect

! This test checks the lowering of atomic write

!FIRDialect: func @_QQmain() {
!FIRDialect: %[[VAR_X:.*]] = fir.alloca i32 {bindc_name = "x", uniq_name = "_QFEx"}
!FIRDialect: %[[VAR_Y:.*]] = fir.alloca i32 {bindc_name = "y", uniq_name = "_QFEy"}
!FIRDialect: %[[VAR_Z:.*]] = fir.alloca i32 {bindc_name = "z", uniq_name = "_QFEz"}
!FIRDialect: %[[CONST_44:.*]] = arith.constant 44 : i32
!FIRDialect: omp.atomic.write %[[VAR_X]], %[[CONST_44]] memory_order(seq_cst) hint(uncontended) : !fir.ref<i32>, i32
!FIRDialect: %[[CONST_7:.*]] = arith.constant 7 : i32
!FIRDialect: {{.*}} = fir.load %[[VAR_Y]] : !fir.ref<i32>
!FIRDialect: %[[VAR_7y:.*]] = arith.muli %c7_i32, %3 : i32
!FIRDialect: omp.atomic.write %[[VAR_X]], %[[VAR_7y]] memory_order(relaxed) : !fir.ref<i32>, i32
!FIRDialect: %[[CONST_10:.*]] = arith.constant 10 : i32
!FIRDialect: {{.*}} = fir.load %[[VAR_X]] : !fir.ref<i32>
!FIRDialect: {{.*}} = arith.muli %[[CONST_10]], {{.*}} : i32
!FIRDialect: {{.*}} = fir.load %[[VAR_Z]] : !fir.ref<i32>
!FIRDialect: %[[CONST_2:.*]] = arith.constant 2 : i32
!FIRDialect: {{.*}} = arith.divsi {{.*}}, %[[CONST_2]] : i32
!FIRDialect: {{.*}} = arith.addi {{.*}}, {{.*}} : i32
!FIRDialect: omp.atomic.write %[[VAR_Y]], {{.*}} memory_order(release) hint(speculative) : !fir.ref<i32>, i32
!FIRDialect: return
!FIRDialect: }

!LLVMIRDialect: llvm.func @_QQmain() {
!LLVMIRDialect: %[[LLVM_VAR_44:.*]] = llvm.mlir.constant(44 : i32) : i32
!LLVMIRDialect: %[[LLVM_VAR_7:.*]] = llvm.mlir.constant(7 : i32) : i32
!LLVMIRDialect: %[[LLVM_VAR_10:.*]] = llvm.mlir.constant(10 : i32) : i32
!LLVMIRDialect: %[[LLVM_VAR_2:.*]] = llvm.mlir.constant(2 : i32) : i32
!LLVMIRDialect: %[[LLVM_VAR_1:.*]] = llvm.mlir.constant(1 : i64) : i64
!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>
!LLVMIRDialect: %[[LLVM_VAR_1_SECOND:.*]] = llvm.mlir.constant(1 : i64) : i64
!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>
!LLVMIRDialect: %[[LLVM_VAR_1_THIRD:.*]] = llvm.mlir.constant(1 : i64) : i64
!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>
!LLVMIRDialect: omp.atomic.write %[[LLVM_VAR_X]], %[[LLVM_VAR_44]] memory_order(seq_cst) hint(uncontended) : !llvm.ptr<i32>, i32
!LLVMIRDialect: {{.*}} = llvm.load %[[LLVM_VAR_Y]] : !llvm.ptr<i32>
!LLVMIRDialect: %[[LLVM_VAR_MUL_RESULT:.*]] = llvm.mul {{.*}}, %[[LLVM_VAR_7]] : i32
!LLVMIRDialect: omp.atomic.write %[[LLVM_VAR_X]], %[[LLVM_VAR_MUL_RESULT]] memory_order(relaxed) : !llvm.ptr<i32>, i32
!LLVMIRDialect: {{.*}} = llvm.load %[[LLVM_VAR_X]] : !llvm.ptr<i32>
!LLVMIRDialect: {{.*}} = llvm.mul {{.*}}, %[[LLVM_VAR_10]] : i32
!LLVMIRDialect: {{.*}} = llvm.load %[[LLVM_VAR_Z]] : !llvm.ptr<i32>
!LLVMIRDialect: {{.*}} = llvm.sdiv {{.*}}, %[[LLVM_VAR_2]] : i32
!LLVMIRDialect: {{.*}} = llvm.add {{.*}} : i32
!LLVMIRDialect: omp.atomic.write %[[LLVM_VAR_Y]], {{.*}} memory_order(release) hint(speculative) : !llvm.ptr<i32>, i32
!LLVMIRDialect: llvm.return
!LLVMIRDialect: }

program OmpAtomicWrite
use omp_lib
integer :: x, y, z
!$omp atomic seq_cst write hint(omp_sync_hint_uncontended)
x = 8*4 + 12

!$omp atomic write relaxed
x = 7 * y

!$omp atomic write release hint(omp_sync_hint_speculative)
y = 10*x + z/2
end program OmpAtomicWrite