[flang-commits] [flang] 00c511b - Added lowering support for atomic read and write constructs

Nimish Mishra via flang-commits flang-commits at lists.llvm.org
Wed Apr 20 23:50:04 PDT 2022


Author: Nimish Mishra
Date: 2022-04-21T12:19:13+05:30
New Revision: 00c511b35195616430112e5d537588dfdae42cba

URL: https://github.com/llvm/llvm-project/commit/00c511b35195616430112e5d537588dfdae42cba
DIFF: https://github.com/llvm/llvm-project/commit/00c511b35195616430112e5d537588dfdae42cba.diff

LOG: Added lowering support for atomic read and write constructs

This patch adds lowering support for atomic read and write constructs.
Also added is pointer modelling code to allow FIR pointer like types to
be inferred and converted while lowering.

Reviewed By: kiranchandramohan

Differential Revision: https://reviews.llvm.org/D122725

Co-authored-by: Kiran Chandramohan <kiran.chandramohan at arm.com>

Added: 
    flang/include/flang/Tools/PointerModels.h
    flang/test/Lower/OpenMP/atomic01.f90
    flang/test/Lower/OpenMP/atomic02.f90

Modified: 
    flang/lib/Lower/OpenMP.cpp
    flang/lib/Optimizer/CodeGen/CodeGen.cpp
    flang/lib/Optimizer/Dialect/FIRType.cpp
    mlir/include/mlir/Conversion/OpenMPToLLVM/ConvertOpenMPToLLVM.h
    mlir/lib/Conversion/OpenMPToLLVM/OpenMPToLLVM.cpp

Removed: 
    


################################################################################
diff  --git a/flang/include/flang/Tools/PointerModels.h b/flang/include/flang/Tools/PointerModels.h
new file mode 100644
index 0000000000000..915a7afb18b37
--- /dev/null
+++ b/flang/include/flang/Tools/PointerModels.h
@@ -0,0 +1,34 @@
+//===-- Tools/PointerModels.h --------------------- *-C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef FORTRAN_TOOLS_POINTER_MODELS_H
+#define FORTRAN_TOOLS_POINTER_MODELS_H
+
+#include "mlir/Dialect/OpenMP/OpenMPDialect.h"
+
+/// models for FIR pointer like types that already provide a `getElementType` or
+/// a `getEleTy` 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();
+  }
+};
+
+#endif // FORTRAN_TOOLS_POINTER_MODELS_H

diff  --git a/flang/lib/Lower/OpenMP.cpp b/flang/lib/Lower/OpenMP.cpp
index 66ed6b620ba1c..212b269305d72 100644
--- a/flang/lib/Lower/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP.cpp
@@ -452,6 +452,145 @@ genOMP(Fortran::lower::AbstractConverter &converter,
   }
 }
 
+static void genOmpAtomicHintAndMemoryOrderClauses(
+    Fortran::lower::AbstractConverter &converter,
+    const Fortran::parser::OmpAtomicClauseList &clauseList,
+    mlir::IntegerAttr &hint,
+    mlir::omp::ClauseMemoryOrderKindAttr &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);
+        uint64_t hintExprValue = *Fortran::evaluate::ToInt64(*expr);
+        hint = firOpBuilder.getI64IntegerAttr(hintExprValue);
+      }
+    } 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 = mlir::omp::ClauseMemoryOrderKindAttr::get(
+            firOpBuilder.getContext(), omp::ClauseMemoryOrderKind::Acquire);
+      } else if (std::get_if<Fortran::parser::OmpClause::Relaxed>(
+                     &ompMemoryOrderClause->v.u)) {
+        memory_order = mlir::omp::ClauseMemoryOrderKindAttr::get(
+            firOpBuilder.getContext(), omp::ClauseMemoryOrderKind::Relaxed);
+      } else if (std::get_if<Fortran::parser::OmpClause::SeqCst>(
+                     &ompMemoryOrderClause->v.u)) {
+        memory_order = mlir::omp::ClauseMemoryOrderKindAttr::get(
+            firOpBuilder.getContext(), omp::ClauseMemoryOrderKind::Seq_cst);
+      } else if (std::get_if<Fortran::parser::OmpClause::Release>(
+                     &ompMemoryOrderClause->v.u)) {
+        memory_order = mlir::omp::ClauseMemoryOrderKindAttr::get(
+            firOpBuilder.getContext(), 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.
+  mlir::IntegerAttr hint = nullptr;
+  mlir::omp::ClauseMemoryOrderKindAttr 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::Value to_address;
+  mlir::Value from_address;
+  // 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;
+  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);
+  const auto &assignmentStmtVariable = std::get<Fortran::parser::Variable>(
+      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())) {
+      from_address = converter.getSymbolAddress(*name->symbol);
+    }
+  }
+
+  if (auto varDesignator = std::get_if<
+          Fortran::common::Indirection<Fortran::parser::Designator>>(
+          &assignmentStmtVariable.u)) {
+    if (const auto *name = getDesignatorNameIfDataRef(varDesignator->value())) {
+      to_address = converter.getSymbolAddress(*name->symbol);
+    }
+  }
+
+  genOmpAtomicHintAndMemoryOrderClauses(converter, leftHandClauseList, hint,
+                                        memory_order);
+  genOmpAtomicHintAndMemoryOrderClauses(converter, rightHandClauseList, hint,
+                                        memory_order);
+  firOpBuilder.create<mlir::omp::AtomicReadOp>(currentLocation, from_address,
+                                               to_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);
+}
+
 void Fortran::lower::genOpenMPConstruct(
     Fortran::lower::AbstractConverter &converter,
     Fortran::lower::pft::Evaluation &eval,
@@ -485,7 +624,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) {

diff  --git a/flang/lib/Optimizer/CodeGen/CodeGen.cpp b/flang/lib/Optimizer/CodeGen/CodeGen.cpp
index 4bfe0481e6a6e..41dd2a3df8505 100644
--- a/flang/lib/Optimizer/CodeGen/CodeGen.cpp
+++ b/flang/lib/Optimizer/CodeGen/CodeGen.cpp
@@ -3371,11 +3371,9 @@ class FIRToLLVMLowering : public fir::FIRToLLVMLoweringBase<FIRToLLVMLowering> {
     target.addLegalDialect<mlir::LLVM::LLVMDialect>();
     // The OpenMP dialect is legal for Operations without regions, for those
     // which contains regions it is legal if the region contains only the
-    // LLVM dialect.
-    target.addDynamicallyLegalOp<mlir::omp::ParallelOp, mlir::omp::WsLoopOp,
-                                 mlir::omp::MasterOp>([&](Operation *op) {
-      return typeConverter.isLegal(&op->getRegion(0));
-    });
+    // LLVM dialect. Add OpenMP dialect as a legal dialect for conversion and
+    // legalize conversion of OpenMP operations without regions.
+    mlir::configureOpenMPToLLVMConversionLegality(target, typeConverter);
     target.addLegalDialect<mlir::omp::OpenMPDialect>();
 
     // required NOPs for applying a full conversion

diff  --git a/flang/lib/Optimizer/Dialect/FIRType.cpp b/flang/lib/Optimizer/Dialect/FIRType.cpp
index daa34038df53a..559b34edfef38 100644
--- a/flang/lib/Optimizer/Dialect/FIRType.cpp
+++ b/flang/lib/Optimizer/Dialect/FIRType.cpp
@@ -12,6 +12,7 @@
 
 #include "flang/Optimizer/Dialect/FIRType.h"
 #include "flang/Optimizer/Dialect/FIRDialect.h"
+#include "flang/Tools/PointerModels.h"
 #include "mlir/IR/Builders.h"
 #include "mlir/IR/BuiltinDialect.h"
 #include "mlir/IR/Diagnostics.h"
@@ -904,4 +905,15 @@ void FIROpsDialect::registerTypes() {
            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());
 }

diff  --git a/flang/test/Lower/OpenMP/atomic01.f90 b/flang/test/Lower/OpenMP/atomic01.f90
new file mode 100644
index 0000000000000..082181d8ade96
--- /dev/null
+++ b/flang/test/Lower/OpenMP/atomic01.f90
@@ -0,0 +1,74 @@
+! RUN: bbc -fopenmp -emit-fir %s -o - | \
+! RUN: FileCheck %s --check-prefix=FIRDialect
+! RUN: bbc -fopenmp %s -o - | fir-opt --fir-to-llvm-ir | \
+! RUN: FileCheck %s --check-prefix=LLVMDialect
+
+! This test checks the lowering of atomic read
+
+!FIRDialect: func @_QQmain() {
+!FIRDialect: %[[VAR_A:.*]] = fir.address_of(@_QFEa) : !fir.ref<!fir.char<1>>
+!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_E:.*]] = fir.address_of(@_QFEe) : !fir.ref<!fir.char<1,8>>
+!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_X]] = %[[VAR_Y]] memory_order(acquire)  hint(uncontended) : !fir.ref<i32>
+!FIRDialect: omp.atomic.read %[[VAR_A]] = %[[VAR_B]] memory_order(relaxed) hint(none)  : !fir.ref<!fir.char<1>>
+!FIRDialect: omp.atomic.read %[[VAR_C]] = %[[VAR_D]] memory_order(seq_cst)  hint(contended) : !fir.ref<!fir.logical<4>>
+!FIRDialect: omp.atomic.read %[[VAR_E]] = %[[VAR_F]] hint(speculative) : !fir.ref<!fir.char<1,8>>
+!FIRDialect: omp.atomic.read %[[VAR_G]] = %[[VAR_H]] hint(nonspeculative) : !fir.ref<f32>
+!FIRDialect: omp.atomic.read %[[VAR_G]] = %[[VAR_H]] : !fir.ref<f32>
+!FIRDialect: return
+!FIRDialect: }
+
+!LLVMDialect: llvm.func @_QQmain() {
+!LLVMDialect: %[[LLVM_VAR_A:.*]] = llvm.mlir.addressof @_QFEa : !llvm.ptr<array<1 x i8>>
+!LLVMDialect: %[[LLVM_VAR_B:.*]] = llvm.mlir.addressof @_QFEb : !llvm.ptr<array<1 x i8>>
+!LLVMDialect: {{.*}} = llvm.mlir.constant(1 : i64) : i64
+!LLVMDialect:  %[[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>
+!LLVMDialect: {{.*}} = llvm.mlir.constant(1 : i64) : i64
+!LLVMDialect: %[[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>
+!LLVMDialect: %[[LLVM_VAR_E:.*]] = llvm.mlir.addressof @_QFEe : !llvm.ptr<array<8 x i8>>
+!LLVMDialect: %[[LLVM_VAR_F:.*]] = llvm.mlir.addressof @_QFEf : !llvm.ptr<array<8 x i8>>
+!LLVMDialect: {{.*}} = llvm.mlir.constant(1 : i64) : i64
+!LLVMDialect: %[[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>
+!LLVMDialect: {{.*}} = llvm.mlir.constant(1 : i64) : i64
+!LLVMDialect: %[[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>
+!LLVMDialect: {{.*}} = llvm.mlir.constant(1 : i64) : i64
+!LLVMDialect: %[[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>
+!LLVMDialect: {{.*}} = llvm.mlir.constant(1 : i64) : i64
+!LLVMDialect: %[[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>
+!LLVMDialect: omp.atomic.read %[[LLVM_VAR_X]] = %[[LLVM_VAR_Y]] memory_order(acquire)  hint(uncontended) : !llvm.ptr<i32>
+!LLVMDialect: omp.atomic.read %[[LLVM_VAR_A]] =  %[[LLVM_VAR_B]] memory_order(relaxed) hint(none) : !llvm.ptr<array<1 x i8>>
+!LLVMDialect: omp.atomic.read %[[LLVM_VAR_C]] = %[[LLVM_VAR_D]] memory_order(seq_cst)  hint(contended) : !llvm.ptr<i32>
+!LLVMDialect: omp.atomic.read %[[LLVM_VAR_E]] = %[[LLVM_VAR_F]] hint(speculative) : !llvm.ptr<array<8 x i8>>
+!LLVMDialect: omp.atomic.read %[[LLVM_VAR_G]] = %[[LLVM_VAR_H]] hint(nonspeculative) : !llvm.ptr<f32>
+!LLVMDialect: omp.atomic.read %[[LLVM_VAR_G]] = %[[LLVM_VAR_H]] : !llvm.ptr<f32>
+!LLVMDialect: llvm.return
+!LLVMDialect: }
+
+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 = h
+end program OmpAtomic

diff  --git a/flang/test/Lower/OpenMP/atomic02.f90 b/flang/test/Lower/OpenMP/atomic02.f90
new file mode 100644
index 0000000000000..f5ef943e48e14
--- /dev/null
+++ b/flang/test/Lower/OpenMP/atomic02.f90
@@ -0,0 +1,64 @@
+! RUN: bbc -fopenmp -emit-fir %s -o - | \
+! RUN: FileCheck %s --check-prefix=FIRDialect
+! RUN: bbc -fopenmp %s -o - | fir-opt --fir-to-llvm-ir | \
+! RUN: FileCheck %s --check-prefix=LLVMDialect
+
+! 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]] hint(uncontended) memory_order(seq_cst) : !fir.ref<i32>, i32
+!FIRDialect: %[[CONST_7:.*]] = arith.constant 7 : i32
+!FIRDialect: {{.*}} = fir.load %[[VAR_Y]] : !fir.ref<i32>
+!FIRDialect: %[[VAR_7y:.*]] = arith.muli %[[CONST_7]], {{.*}} : 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]] = {{.*}} hint(speculative) memory_order(release) : !fir.ref<i32>, i32
+!FIRDialect: return
+!FIRDialect: }
+
+!LLVMDialect: llvm.func @_QQmain() {
+!LLVMDialect: %[[LLVM_VAR_2:.*]] = llvm.mlir.constant(2 : i32) : i32
+!LLVMDialect: %[[LLVM_VAR_10:.*]] = llvm.mlir.constant(10 : i32) : i32
+!LLVMDialect: %[[LLVM_VAR_7:.*]] = llvm.mlir.constant(7 : i32) : i32
+!LLVMDialect: %[[LLVM_VAR_44:.*]] = llvm.mlir.constant(44 : i32) : i32
+!LLVMDialect: %[[LLVM_VAR_1:.*]] = llvm.mlir.constant(1 : i64) : i64
+!LLVMDialect: %[[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>
+!LLVMDialect: %[[LLVM_VAR_1_SECOND:.*]] = llvm.mlir.constant(1 : i64) : i64
+!LLVMDialect: %[[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>
+!LLVMDialect: %[[LLVM_VAR_1_THIRD:.*]] = llvm.mlir.constant(1 : i64) : i64
+!LLVMDialect: %[[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>
+!LLVMDialect: omp.atomic.write %[[LLVM_VAR_X]] = %[[LLVM_VAR_44]] hint(uncontended) memory_order(seq_cst) : !llvm.ptr<i32>, i32
+!LLVMDialect: {{.*}} = llvm.load %[[LLVM_VAR_Y]] : !llvm.ptr<i32>
+!LLVMDialect: %[[LLVM_VAR_MUL_RESULT:.*]] = llvm.mul {{.*}}, %[[LLVM_VAR_7]] : i32
+!LLVMDialect: omp.atomic.write %[[LLVM_VAR_X]] = %[[LLVM_VAR_MUL_RESULT]] memory_order(relaxed) : !llvm.ptr<i32>, i32
+!LLVMDialect: {{.*}} = llvm.load %[[LLVM_VAR_X]] : !llvm.ptr<i32>
+!LLVMDialect: {{.*}} = llvm.mul {{.*}}, %[[LLVM_VAR_10]] : i32
+!LLVMDialect: {{.*}} = llvm.load %[[LLVM_VAR_Z]] : !llvm.ptr<i32>
+!LLVMDialect: {{.*}} = llvm.sdiv {{.*}}, %[[LLVM_VAR_2]] : i32
+!LLVMDialect: {{.*}} = llvm.add {{.*}} : i32
+!LLVMDialect: omp.atomic.write %[[LLVM_VAR_Y]] = {{.*}} hint(speculative) memory_order(release) : !llvm.ptr<i32>, i32
+!LLVMDialect: llvm.return
+!LLVMDialect: }
+
+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

diff  --git a/mlir/include/mlir/Conversion/OpenMPToLLVM/ConvertOpenMPToLLVM.h b/mlir/include/mlir/Conversion/OpenMPToLLVM/ConvertOpenMPToLLVM.h
index 3793c91992172..b27cd407e0cc9 100644
--- a/mlir/include/mlir/Conversion/OpenMPToLLVM/ConvertOpenMPToLLVM.h
+++ b/mlir/include/mlir/Conversion/OpenMPToLLVM/ConvertOpenMPToLLVM.h
@@ -12,12 +12,18 @@
 
 namespace mlir {
 class LLVMTypeConverter;
+class ConversionTarget;
 class MLIRContext;
 class ModuleOp;
 template <typename T>
 class OperationPass;
 class RewritePatternSet;
 
+/// Configure dynamic conversion legality of regionless operations from OpenMP
+/// to LLVM.
+void configureOpenMPToLLVMConversionLegality(ConversionTarget &target,
+                                             LLVMTypeConverter &typeConverter);
+
 /// Populate the given list with patterns that convert from OpenMP to LLVM.
 void populateOpenMPToLLVMConversionPatterns(LLVMTypeConverter &converter,
                                             RewritePatternSet &patterns);

diff  --git a/mlir/lib/Conversion/OpenMPToLLVM/OpenMPToLLVM.cpp b/mlir/lib/Conversion/OpenMPToLLVM/OpenMPToLLVM.cpp
index ddbb0517a003c..d16e51e0eb24d 100644
--- a/mlir/lib/Conversion/OpenMPToLLVM/OpenMPToLLVM.cpp
+++ b/mlir/lib/Conversion/OpenMPToLLVM/OpenMPToLLVM.cpp
@@ -45,13 +45,39 @@ struct RegionOpConversion : public ConvertOpToLLVMPattern<OpType> {
     return success();
   }
 };
+
+template <typename T>
+struct RegionLessOpConversion : public ConvertOpToLLVMPattern<T> {
+  using ConvertOpToLLVMPattern<T>::ConvertOpToLLVMPattern;
+  LogicalResult
+  matchAndRewrite(T curOp, typename T::Adaptor adaptor,
+                  ConversionPatternRewriter &rewriter) const override {
+    rewriter.replaceOpWithNewOp<T>(curOp, TypeRange(), adaptor.getOperands(),
+                                   curOp->getAttrs());
+    return success();
+  }
+};
 } // namespace
 
+void mlir::configureOpenMPToLLVMConversionLegality(
+    ConversionTarget &target, LLVMTypeConverter &typeConverter) {
+  target.addDynamicallyLegalOp<mlir::omp::ParallelOp, mlir::omp::WsLoopOp,
+                               mlir::omp::MasterOp>(
+      [&](Operation *op) { return typeConverter.isLegal(&op->getRegion(0)); });
+  target
+      .addDynamicallyLegalOp<mlir::omp::AtomicReadOp, mlir::omp::AtomicWriteOp>(
+          [&](Operation *op) {
+            return typeConverter.isLegal(op->getOperandTypes());
+          });
+}
+
 void mlir::populateOpenMPToLLVMConversionPatterns(LLVMTypeConverter &converter,
                                                   RewritePatternSet &patterns) {
   patterns.add<RegionOpConversion<omp::MasterOp>,
                RegionOpConversion<omp::ParallelOp>,
-               RegionOpConversion<omp::WsLoopOp>>(converter);
+               RegionOpConversion<omp::WsLoopOp>,
+               RegionLessOpConversion<omp::AtomicReadOp>,
+               RegionLessOpConversion<omp::AtomicWriteOp>>(converter);
 }
 
 namespace {
@@ -74,10 +100,9 @@ void ConvertOpenMPToLLVMPass::runOnOperation() {
   populateOpenMPToLLVMConversionPatterns(converter, patterns);
 
   LLVMConversionTarget target(getContext());
-  target.addDynamicallyLegalOp<omp::MasterOp, omp::ParallelOp, omp::WsLoopOp>(
-      [&](Operation *op) { return converter.isLegal(&op->getRegion(0)); });
   target.addLegalOp<omp::TerminatorOp, omp::TaskyieldOp, omp::FlushOp,
                     omp::BarrierOp, omp::TaskwaitOp>();
+  configureOpenMPToLLVMConversionLegality(target, converter);
   if (failed(applyPartialConversion(module, target, std::move(patterns))))
     signalPassFailure();
 }


        


More information about the flang-commits mailing list