Skip to content

Commit

Permalink
Added OpenMP 5.0 specification based lowering for atomic read and wri…
Browse files Browse the repository at this point in the history
…te constructs
  • Loading branch information
NimishMishra committed Mar 30, 2022
1 parent 2233dbf commit 3ea6481
Show file tree
Hide file tree
Showing 4 changed files with 300 additions and 1 deletion.
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)) {
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();
}
};

} // 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>>(
*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

! 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

0 comments on commit 3ea6481

Please sign in to comment.