[flang-commits] [clang] [flang] [llvm] [OpenMPIRBuilder] Emit __atomic_load and __atomic_compare_exchange libcalls for complex types in atomic update (PR #92364)

via flang-commits flang-commits at lists.llvm.org
Sat Jul 6 02:45:40 PDT 2024


https://github.com/NimishMishra updated https://github.com/llvm/llvm-project/pull/92364

>From 6423811853ab17f7109a4de972b66ce37573e979 Mon Sep 17 00:00:00 2001
From: Nimish Mishra <neelam.nimish at gmail.com>
Date: Sat, 6 Jul 2024 15:14:32 +0530
Subject: [PATCH] [clang][flang][OpenMPIRBuilder] Emit __atomic_load and
 __atomic_compare_exchange libcalls for complex types in atomic update

---
 clang/lib/CodeGen/CGAtomic.cpp                | 673 +++++++++---------
 .../OpenMP/atomic-update-complex.f90          |  42 ++
 llvm/include/llvm/Frontend/Atomic/Atomic.h    | 235 ++++++
 .../llvm/Frontend/OpenMP/OMPIRBuilder.h       |  24 +
 llvm/lib/Frontend/Atomic/Atomic.cpp           |  18 +
 llvm/lib/Frontend/Atomic/CMakeLists.txt       |  15 +
 llvm/lib/Frontend/CMakeLists.txt              |   1 +
 llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp     |  47 ++
 8 files changed, 701 insertions(+), 354 deletions(-)
 create mode 100644 flang/test/Integration/OpenMP/atomic-update-complex.f90
 create mode 100644 llvm/include/llvm/Frontend/Atomic/Atomic.h
 create mode 100644 llvm/lib/Frontend/Atomic/Atomic.cpp
 create mode 100644 llvm/lib/Frontend/Atomic/CMakeLists.txt

diff --git a/clang/lib/CodeGen/CGAtomic.cpp b/clang/lib/CodeGen/CGAtomic.cpp
index fbf942d06ca6e..a7bbbd20cc31b 100644
--- a/clang/lib/CodeGen/CGAtomic.cpp
+++ b/clang/lib/CodeGen/CGAtomic.cpp
@@ -19,6 +19,7 @@
 #include "clang/CodeGen/CGFunctionInfo.h"
 #include "clang/Frontend/FrontendDiagnostic.h"
 #include "llvm/ADT/DenseMap.h"
+#include "llvm/Frontend/Atomic/Atomic.h"
 #include "llvm/IR/DataLayout.h"
 #include "llvm/IR/Intrinsics.h"
 #include "llvm/IR/Operator.h"
@@ -27,275 +28,322 @@ using namespace clang;
 using namespace CodeGen;
 
 namespace {
-  class AtomicInfo {
-    CodeGenFunction &CGF;
-    QualType AtomicTy;
+class AtomicInfo : public llvm::AtomicInfo<CGBuilderBaseTy> {
+  CodeGenFunction &CGF;
+  QualType AtomicTy;
+  QualType ValueTy;
+  TypeEvaluationKind EvaluationKind;
+  LValue LVal;
+  CGBitFieldInfo BFI;
+
+public:
+  llvm::AtomicInfo<CGBuilderBaseTy> &getBase() { return *this; }
+
+  static AtomicInfo fromValue(CodeGenFunction &CGF, LValue &lvalue) {
+    if (lvalue.isSimple())
+      return fromSimple(CGF, lvalue);
+    if (lvalue.isBitField())
+      return fromBitfield(CGF, lvalue);
+    if (lvalue.isVectorElt())
+      return fromVectorElt(CGF, lvalue);
+    if (lvalue.isExtVectorElt())
+      return fromExtVectorElt(CGF, lvalue);
+    llvm_unreachable("Type incompatible with atomics");
+  }
+
+  static AtomicInfo fromSimple(CodeGenFunction &CGF, LValue &lvalue) {
+    assert(lvalue.isSimple());
+    ASTContext &C = CGF.getContext();
+
+    QualType AtomicTy = lvalue.getType();
     QualType ValueTy;
-    uint64_t AtomicSizeInBits;
-    uint64_t ValueSizeInBits;
-    CharUnits AtomicAlign;
-    CharUnits ValueAlign;
-    TypeEvaluationKind EvaluationKind;
-    bool UseLibcall;
-    LValue LVal;
-    CGBitFieldInfo BFI;
-  public:
-    AtomicInfo(CodeGenFunction &CGF, LValue &lvalue)
-        : CGF(CGF), AtomicSizeInBits(0), ValueSizeInBits(0),
-          EvaluationKind(TEK_Scalar), UseLibcall(true) {
-      assert(!lvalue.isGlobalReg());
-      ASTContext &C = CGF.getContext();
-      if (lvalue.isSimple()) {
-        AtomicTy = lvalue.getType();
-        if (auto *ATy = AtomicTy->getAs<AtomicType>())
-          ValueTy = ATy->getValueType();
-        else
-          ValueTy = AtomicTy;
-        EvaluationKind = CGF.getEvaluationKind(ValueTy);
-
-        uint64_t ValueAlignInBits;
-        uint64_t AtomicAlignInBits;
-        TypeInfo ValueTI = C.getTypeInfo(ValueTy);
-        ValueSizeInBits = ValueTI.Width;
-        ValueAlignInBits = ValueTI.Align;
-
-        TypeInfo AtomicTI = C.getTypeInfo(AtomicTy);
-        AtomicSizeInBits = AtomicTI.Width;
-        AtomicAlignInBits = AtomicTI.Align;
-
-        assert(ValueSizeInBits <= AtomicSizeInBits);
-        assert(ValueAlignInBits <= AtomicAlignInBits);
-
-        AtomicAlign = C.toCharUnitsFromBits(AtomicAlignInBits);
-        ValueAlign = C.toCharUnitsFromBits(ValueAlignInBits);
-        if (lvalue.getAlignment().isZero())
-          lvalue.setAlignment(AtomicAlign);
-
-        LVal = lvalue;
-      } else if (lvalue.isBitField()) {
-        ValueTy = lvalue.getType();
-        ValueSizeInBits = C.getTypeSize(ValueTy);
-        auto &OrigBFI = lvalue.getBitFieldInfo();
-        auto Offset = OrigBFI.Offset % C.toBits(lvalue.getAlignment());
-        AtomicSizeInBits = C.toBits(
-            C.toCharUnitsFromBits(Offset + OrigBFI.Size + C.getCharWidth() - 1)
-                .alignTo(lvalue.getAlignment()));
-        llvm::Value *BitFieldPtr = lvalue.getRawBitFieldPointer(CGF);
-        auto OffsetInChars =
-            (C.toCharUnitsFromBits(OrigBFI.Offset) / lvalue.getAlignment()) *
-            lvalue.getAlignment();
-        llvm::Value *StoragePtr = CGF.Builder.CreateConstGEP1_64(
-            CGF.Int8Ty, BitFieldPtr, OffsetInChars.getQuantity());
-        StoragePtr = CGF.Builder.CreateAddrSpaceCast(
-            StoragePtr, CGF.UnqualPtrTy, "atomic_bitfield_base");
-        BFI = OrigBFI;
-        BFI.Offset = Offset;
-        BFI.StorageSize = AtomicSizeInBits;
-        BFI.StorageOffset += OffsetInChars;
-        llvm::Type *StorageTy = CGF.Builder.getIntNTy(AtomicSizeInBits);
-        LVal = LValue::MakeBitfield(
-            Address(StoragePtr, StorageTy, lvalue.getAlignment()), BFI,
-            lvalue.getType(), lvalue.getBaseInfo(), lvalue.getTBAAInfo());
-        AtomicTy = C.getIntTypeForBitwidth(AtomicSizeInBits, OrigBFI.IsSigned);
-        if (AtomicTy.isNull()) {
-          llvm::APInt Size(
-              /*numBits=*/32,
-              C.toCharUnitsFromBits(AtomicSizeInBits).getQuantity());
-          AtomicTy = C.getConstantArrayType(C.CharTy, Size, nullptr,
-                                            ArraySizeModifier::Normal,
-                                            /*IndexTypeQuals=*/0);
-        }
-        AtomicAlign = ValueAlign = lvalue.getAlignment();
-      } else if (lvalue.isVectorElt()) {
-        ValueTy = lvalue.getType()->castAs<VectorType>()->getElementType();
-        ValueSizeInBits = C.getTypeSize(ValueTy);
-        AtomicTy = lvalue.getType();
-        AtomicSizeInBits = C.getTypeSize(AtomicTy);
-        AtomicAlign = ValueAlign = lvalue.getAlignment();
-        LVal = lvalue;
-      } else {
-        assert(lvalue.isExtVectorElt());
-        ValueTy = lvalue.getType();
-        ValueSizeInBits = C.getTypeSize(ValueTy);
-        AtomicTy = ValueTy = CGF.getContext().getExtVectorType(
-            lvalue.getType(), cast<llvm::FixedVectorType>(
-                                  lvalue.getExtVectorAddress().getElementType())
-                                  ->getNumElements());
-        AtomicSizeInBits = C.getTypeSize(AtomicTy);
-        AtomicAlign = ValueAlign = lvalue.getAlignment();
-        LVal = lvalue;
-      }
-      UseLibcall = !C.getTargetInfo().hasBuiltinAtomic(
-          AtomicSizeInBits, C.toBits(lvalue.getAlignment()));
-    }
+    if (auto *ATy = AtomicTy->getAs<AtomicType>())
+      ValueTy = ATy->getValueType();
+    else
+      ValueTy = AtomicTy;
+    TypeEvaluationKind EvaluationKind = CGF.getEvaluationKind(ValueTy);
 
-    QualType getAtomicType() const { return AtomicTy; }
-    QualType getValueType() const { return ValueTy; }
-    CharUnits getAtomicAlignment() const { return AtomicAlign; }
-    uint64_t getAtomicSizeInBits() const { return AtomicSizeInBits; }
-    uint64_t getValueSizeInBits() const { return ValueSizeInBits; }
-    TypeEvaluationKind getEvaluationKind() const { return EvaluationKind; }
-    bool shouldUseLibcall() const { return UseLibcall; }
-    const LValue &getAtomicLValue() const { return LVal; }
-    llvm::Value *getAtomicPointer() const {
-      if (LVal.isSimple())
-        return LVal.emitRawPointer(CGF);
-      else if (LVal.isBitField())
-        return LVal.getRawBitFieldPointer(CGF);
-      else if (LVal.isVectorElt())
-        return LVal.getRawVectorPointer(CGF);
-      assert(LVal.isExtVectorElt());
-      return LVal.getRawExtVectorPointer(CGF);
-    }
-    Address getAtomicAddress() const {
-      llvm::Type *ElTy;
-      if (LVal.isSimple())
-        ElTy = LVal.getAddress().getElementType();
-      else if (LVal.isBitField())
-        ElTy = LVal.getBitFieldAddress().getElementType();
-      else if (LVal.isVectorElt())
-        ElTy = LVal.getVectorAddress().getElementType();
-      else
-        ElTy = LVal.getExtVectorAddress().getElementType();
-      return Address(getAtomicPointer(), ElTy, getAtomicAlignment());
-    }
+    TypeInfo ValueTI = C.getTypeInfo(ValueTy);
+    auto ValueSizeInBits = ValueTI.Width;
+    auto ValueAlignInBits = ValueTI.Align;
 
-    Address getAtomicAddressAsAtomicIntPointer() const {
-      return castToAtomicIntPointer(getAtomicAddress());
-    }
+    TypeInfo AtomicTI = C.getTypeInfo(AtomicTy);
+    auto AtomicSizeInBits = AtomicTI.Width;
+    auto AtomicAlignInBits = AtomicTI.Align;
 
-    /// Is the atomic size larger than the underlying value type?
-    ///
-    /// Note that the absence of padding does not mean that atomic
-    /// objects are completely interchangeable with non-atomic
-    /// objects: we might have promoted the alignment of a type
-    /// without making it bigger.
-    bool hasPadding() const {
-      return (ValueSizeInBits != AtomicSizeInBits);
-    }
+    assert(ValueSizeInBits <= AtomicSizeInBits);
+    assert(ValueAlignInBits <= AtomicAlignInBits);
+
+    auto AtomicAlign = C.toCharUnitsFromBits(AtomicAlignInBits).getAsAlign();
+    auto ValueAlign = C.toCharUnitsFromBits(ValueAlignInBits).getAsAlign();
+    if (lvalue.getAlignment().isZero())
+      lvalue.setAlignment(CharUnits::fromQuantity(AtomicAlign));
+
+    auto LVal = lvalue;
+    auto ElTy = LVal.getAddress().getElementType();
 
-    bool emitMemSetZeroIfNecessary() const;
+    bool UseLibcall = !C.getTargetInfo().hasBuiltinAtomic(
+        AtomicSizeInBits, C.toBits(lvalue.getAlignment()));
+    return AtomicInfo(CGF, ElTy, LVal, AtomicTy, ValueTy, EvaluationKind, {},
+                      AtomicSizeInBits, ValueSizeInBits, AtomicAlign,
+                      ValueAlign, UseLibcall);
+  }
 
-    llvm::Value *getAtomicSizeValue() const {
-      CharUnits size = CGF.getContext().toCharUnitsFromBits(AtomicSizeInBits);
-      return CGF.CGM.getSize(size);
+  static AtomicInfo fromBitfield(CodeGenFunction &CGF, LValue &lvalue) {
+    assert(lvalue.isBitField());
+    ASTContext &C = CGF.getContext();
+
+    auto ValueTy = lvalue.getType();
+    auto ValueSizeInBits = C.getTypeSize(ValueTy);
+    auto &OrigBFI = lvalue.getBitFieldInfo();
+    auto Offset = OrigBFI.Offset % C.toBits(lvalue.getAlignment());
+    auto AtomicSizeInBits = C.toBits(
+        C.toCharUnitsFromBits(Offset + OrigBFI.Size + C.getCharWidth() - 1)
+            .alignTo(lvalue.getAlignment()));
+    llvm::Value *BitFieldPtr = lvalue.getRawBitFieldPointer(CGF);
+    auto OffsetInChars =
+        (C.toCharUnitsFromBits(OrigBFI.Offset) / lvalue.getAlignment()) *
+        lvalue.getAlignment();
+    llvm::Value *StoragePtr = CGF.Builder.CreateConstGEP1_64(
+        CGF.Int8Ty, BitFieldPtr, OffsetInChars.getQuantity());
+    StoragePtr = CGF.Builder.CreateAddrSpaceCast(StoragePtr, CGF.UnqualPtrTy,
+                                                 "atomic_bitfield_base");
+    auto BFI = OrigBFI;
+    BFI.Offset = Offset;
+    BFI.StorageSize = AtomicSizeInBits;
+    BFI.StorageOffset += OffsetInChars;
+    llvm::Type *StorageTy = CGF.Builder.getIntNTy(AtomicSizeInBits);
+    auto LVal = LValue::MakeBitfield(
+        Address(StoragePtr, StorageTy, lvalue.getAlignment()), BFI,
+        lvalue.getType(), lvalue.getBaseInfo(), lvalue.getTBAAInfo());
+    auto AtomicTy = C.getIntTypeForBitwidth(AtomicSizeInBits, OrigBFI.IsSigned);
+    if (AtomicTy.isNull()) {
+      llvm::APInt Size(
+          /*numBits=*/32,
+          C.toCharUnitsFromBits(AtomicSizeInBits).getQuantity());
+      AtomicTy = C.getConstantArrayType(C.CharTy, Size, nullptr,
+                                        ArraySizeModifier::Normal,
+                                        /*IndexTypeQuals=*/0);
     }
+    auto ValueAlign = lvalue.getAlignment().getAsAlign();
+    auto AtomicAlign = ValueAlign;
 
-    /// Cast the given pointer to an integer pointer suitable for atomic
-    /// operations if the source.
-    Address castToAtomicIntPointer(Address Addr) const;
+    auto ElTy = LVal.getBitFieldAddress().getElementType();
 
-    /// If Addr is compatible with the iN that will be used for an atomic
-    /// operation, bitcast it. Otherwise, create a temporary that is suitable
-    /// and copy the value across.
-    Address convertToAtomicIntPointer(Address Addr) const;
+    TypeEvaluationKind EvaluationKind = TEK_Scalar;
+    bool UseLibcall = !C.getTargetInfo().hasBuiltinAtomic(
+        AtomicSizeInBits, C.toBits(lvalue.getAlignment()));
+    return AtomicInfo(CGF, ElTy, LVal, AtomicTy, ValueTy, EvaluationKind, {},
+                      AtomicSizeInBits, ValueSizeInBits, AtomicAlign,
+                      ValueAlign, UseLibcall);
+  }
 
-    /// Turn an atomic-layout object into an r-value.
-    RValue convertAtomicTempToRValue(Address addr, AggValueSlot resultSlot,
-                                     SourceLocation loc, bool AsValue) const;
+  static AtomicInfo fromVectorElt(CodeGenFunction &CGF, LValue &lvalue) {
+    assert(lvalue.isVectorElt());
+    ASTContext &C = CGF.getContext();
+
+    auto ValueTy = lvalue.getType()->castAs<VectorType>()->getElementType();
+    auto ValueSizeInBits = C.getTypeSize(ValueTy);
+    auto AtomicTy = lvalue.getType();
+    auto AtomicSizeInBits = C.getTypeSize(AtomicTy);
+    auto ValueAlign = lvalue.getAlignment().getAsAlign();
+    auto AtomicAlign = ValueAlign;
+    auto LVal = lvalue;
+
+    auto ElTy = LVal.getVectorAddress().getElementType();
+
+    TypeEvaluationKind EvaluationKind = TEK_Scalar;
+    bool UseLibcall = !C.getTargetInfo().hasBuiltinAtomic(
+        AtomicSizeInBits,
+        C.toBits(lvalue.getAlignment())); // TODO: Move to llvm::AtomicInfo
+    return AtomicInfo(CGF, ElTy, LVal, AtomicTy, ValueTy, EvaluationKind, {},
+                      AtomicSizeInBits, ValueSizeInBits, AtomicAlign,
+                      ValueAlign, UseLibcall);
+  }
 
-    llvm::Value *getScalarRValValueOrNull(RValue RVal) const;
+  static AtomicInfo fromExtVectorElt(CodeGenFunction &CGF, LValue &lvalue) {
+    assert(lvalue.isExtVectorElt());
+    ASTContext &C = CGF.getContext();
+
+    auto ValueTy = lvalue.getType();
+    auto ValueSizeInBits = C.getTypeSize(ValueTy);
+    auto AtomicTy = ValueTy = CGF.getContext().getExtVectorType(
+        lvalue.getType(), cast<llvm::FixedVectorType>(
+                              lvalue.getExtVectorAddress().getElementType())
+                              ->getNumElements());
+    auto AtomicSizeInBits = C.getTypeSize(AtomicTy);
+    auto ValueAlign = lvalue.getAlignment().getAsAlign();
+    auto AtomicAlign = ValueAlign;
+    auto LVal = lvalue;
+
+    auto ElTy = LVal.getExtVectorAddress().getElementType();
+
+    TypeEvaluationKind EvaluationKind = TEK_Scalar;
+    bool UseLibcall = !C.getTargetInfo().hasBuiltinAtomic(
+        AtomicSizeInBits,
+        C.toBits(lvalue.getAlignment())); // TODO: Move to llvm::AtomicInfo
+    return AtomicInfo(CGF, ElTy, LVal, AtomicTy, ValueTy, EvaluationKind, {},
+                      AtomicSizeInBits, ValueSizeInBits, AtomicAlign,
+                      ValueAlign, UseLibcall);
+  }
 
-    /// Converts an rvalue to integer value if needed.
-    llvm::Value *convertRValueToInt(RValue RVal, bool CmpXchg = false) const;
+  AtomicInfo(CodeGenFunction &CGF, llvm::Type *Ty, LValue &lvalue,
+             QualType AtomicTy, QualType ValueTy,
+             TypeEvaluationKind EvaluationKind, CGBitFieldInfo BFI,
+             uint64_t AtomicSizeInBits, uint64_t ValueSizeInBits,
+             llvm::Align AtomicAlign, llvm::Align ValueAlign, bool UseLibcall)
+      : llvm::AtomicInfo<CGBuilderBaseTy>(&CGF.Builder, Ty, AtomicSizeInBits,
+                                          ValueSizeInBits, AtomicAlign,
+                                          ValueAlign, UseLibcall),
+        CGF(CGF), AtomicTy(AtomicTy), ValueTy(ValueTy),
+        EvaluationKind(EvaluationKind), LVal(lvalue), BFI(BFI) {}
+
+  QualType getAtomicType() const { return AtomicTy; }
+  QualType getValueType() const { return ValueTy; }
+  CharUnits getAtomicAlignment() const {
+    return CharUnits::fromQuantity(AtomicAlign);
+  }
+  TypeEvaluationKind getEvaluationKind() const { return EvaluationKind; }
+  const LValue &getAtomicLValue() const { return LVal; }
+  llvm::Value *getAtomicPointer() const override {
+    if (LVal.isSimple())
+      return LVal.emitRawPointer(CGF);
+    else if (LVal.isBitField())
+      return LVal.getRawBitFieldPointer(CGF);
+    else if (LVal.isVectorElt())
+      return LVal.getRawVectorPointer(CGF);
+    assert(LVal.isExtVectorElt());
+    return LVal.getRawExtVectorPointer(CGF);
+  }
+  Address getAtomicAddress() const {
+    llvm::Type *ElTy;
+    if (LVal.isSimple())
+      ElTy = LVal.getAddress().getElementType();
+    else if (LVal.isBitField())
+      ElTy = LVal.getBitFieldAddress().getElementType();
+    else if (LVal.isVectorElt())
+      ElTy = LVal.getVectorAddress().getElementType();
+    else
+      ElTy = LVal.getExtVectorAddress().getElementType();
+    return Address(getAtomicPointer(), ElTy, getAtomicAlignment());
+  }
 
-    RValue ConvertToValueOrAtomic(llvm::Value *IntVal, AggValueSlot ResultSlot,
-                                  SourceLocation Loc, bool AsValue,
-                                  bool CmpXchg = false) const;
+  void decorateWithTBAA(llvm::Instruction *I) override {
+    CGF.CGM.DecorateInstructionWithTBAA(I, LVal.getTBAAInfo());
+  }
 
-    /// Copy an atomic r-value into atomic-layout memory.
-    void emitCopyIntoMemory(RValue rvalue) const;
+  llvm::AllocaInst *CreateAlloca(llvm::Type *Ty,
+                                 const llvm::Twine &Name) override {
+    return CGF.CreateTempAlloca(Ty, Name);
+  }
 
-    /// Project an l-value down to the value field.
-    LValue projectValue() const {
-      assert(LVal.isSimple());
-      Address addr = getAtomicAddress();
-      if (hasPadding())
-        addr = CGF.Builder.CreateStructGEP(addr, 0);
+  Address getAtomicAddressAsAtomicIntPointer() const {
+    return castToAtomicIntPointer(getAtomicAddress());
+  }
 
-      return LValue::MakeAddr(addr, getValueType(), CGF.getContext(),
-                              LVal.getBaseInfo(), LVal.getTBAAInfo());
-    }
+  bool emitMemSetZeroIfNecessary() const;
 
-    /// Emits atomic load.
-    /// \returns Loaded value.
-    RValue EmitAtomicLoad(AggValueSlot ResultSlot, SourceLocation Loc,
-                          bool AsValue, llvm::AtomicOrdering AO,
-                          bool IsVolatile);
+  llvm::Value *getAtomicSizeValue() const {
+    CharUnits size = CGF.getContext().toCharUnitsFromBits(AtomicSizeInBits);
+    return CGF.CGM.getSize(size);
+  }
+
+  /// Cast the given pointer to an integer pointer suitable for atomic
+  /// operations if the source.
+  Address castToAtomicIntPointer(Address Addr) const;
+
+  /// If Addr is compatible with the iN that will be used for an atomic
+  /// operation, bitcast it. Otherwise, create a temporary that is suitable
+  /// and copy the value across.
+  Address convertToAtomicIntPointer(Address Addr) const;
+
+  /// Turn an atomic-layout object into an r-value.
+  RValue convertAtomicTempToRValue(Address addr, AggValueSlot resultSlot,
+                                   SourceLocation loc, bool AsValue) const;
+
+  llvm::Value *getScalarRValValueOrNull(RValue RVal) const;
+
+  /// Converts an rvalue to integer value if needed.
+  llvm::Value *convertRValueToInt(RValue RVal, bool CmpXchg = false) const;
 
-    /// Emits atomic compare-and-exchange sequence.
-    /// \param Expected Expected value.
-    /// \param Desired Desired value.
-    /// \param Success Atomic ordering for success operation.
-    /// \param Failure Atomic ordering for failed operation.
-    /// \param IsWeak true if atomic operation is weak, false otherwise.
-    /// \returns Pair of values: previous value from storage (value type) and
-    /// boolean flag (i1 type) with true if success and false otherwise.
-    std::pair<RValue, llvm::Value *>
-    EmitAtomicCompareExchange(RValue Expected, RValue Desired,
-                              llvm::AtomicOrdering Success =
-                                  llvm::AtomicOrdering::SequentiallyConsistent,
-                              llvm::AtomicOrdering Failure =
-                                  llvm::AtomicOrdering::SequentiallyConsistent,
-                              bool IsWeak = false);
-
-    /// Emits atomic update.
-    /// \param AO Atomic ordering.
-    /// \param UpdateOp Update operation for the current lvalue.
-    void EmitAtomicUpdate(llvm::AtomicOrdering AO,
+  RValue ConvertToValueOrAtomic(llvm::Value *IntVal, AggValueSlot ResultSlot,
+                                SourceLocation Loc, bool AsValue,
+                                bool CmpXchg = false) const;
+
+  /// Copy an atomic r-value into atomic-layout memory.
+  void emitCopyIntoMemory(RValue rvalue) const;
+
+  /// Project an l-value down to the value field.
+  LValue projectValue() const {
+    assert(LVal.isSimple());
+    Address addr = getAtomicAddress();
+    if (hasPadding())
+      addr = CGF.Builder.CreateStructGEP(addr, 0);
+
+    return LValue::MakeAddr(addr, getValueType(), CGF.getContext(),
+                            LVal.getBaseInfo(), LVal.getTBAAInfo());
+  }
+
+  /// Emits atomic load.
+  /// \returns Loaded value.
+  RValue EmitAtomicLoad(AggValueSlot ResultSlot, SourceLocation Loc,
+                        bool AsValue, llvm::AtomicOrdering AO, bool IsVolatile);
+
+  /// Emits atomic compare-and-exchange sequence.
+  /// \param Expected Expected value.
+  /// \param Desired Desired value.
+  /// \param Success Atomic ordering for success operation.
+  /// \param Failure Atomic ordering for failed operation.
+  /// \param IsWeak true if atomic operation is weak, false otherwise.
+  /// \returns Pair of values: previous value from storage (value type) and
+  /// boolean flag (i1 type) with true if success and false otherwise.
+  std::pair<RValue, llvm::Value *>
+  EmitAtomicCompareExchange(RValue Expected, RValue Desired,
+                            llvm::AtomicOrdering Success =
+                                llvm::AtomicOrdering::SequentiallyConsistent,
+                            llvm::AtomicOrdering Failure =
+                                llvm::AtomicOrdering::SequentiallyConsistent,
+                            bool IsWeak = false);
+
+  /// Emits atomic update.
+  /// \param AO Atomic ordering.
+  /// \param UpdateOp Update operation for the current lvalue.
+  void EmitAtomicUpdate(llvm::AtomicOrdering AO,
+                        const llvm::function_ref<RValue(RValue)> &UpdateOp,
+                        bool IsVolatile);
+  /// Emits atomic update.
+  /// \param AO Atomic ordering.
+  void EmitAtomicUpdate(llvm::AtomicOrdering AO, RValue UpdateRVal,
+                        bool IsVolatile);
+
+  /// Materialize an atomic r-value in atomic-layout memory.
+  Address materializeRValue(RValue rvalue) const;
+
+  /// Creates temp alloca for intermediate operations on atomic value.
+  Address CreateTempAlloca() const;
+
+private:
+  bool requiresMemSetZero(llvm::Type *type) const;
+
+  /// Emits atomic load as a libcall.
+  void EmitAtomicLoadLibcall(llvm::Value *AddForLoaded, llvm::AtomicOrdering AO,
+                             bool IsVolatile);
+  /// Emit atomic update as libcalls.
+  void
+  EmitAtomicUpdateLibcall(llvm::AtomicOrdering AO,
+                          const llvm::function_ref<RValue(RValue)> &UpdateOp,
+                          bool IsVolatile);
+  /// Emit atomic update as LLVM instructions.
+  void EmitAtomicUpdateOp(llvm::AtomicOrdering AO,
                           const llvm::function_ref<RValue(RValue)> &UpdateOp,
                           bool IsVolatile);
-    /// Emits atomic update.
-    /// \param AO Atomic ordering.
-    void EmitAtomicUpdate(llvm::AtomicOrdering AO, RValue UpdateRVal,
+  /// Emit atomic update as libcalls.
+  void EmitAtomicUpdateLibcall(llvm::AtomicOrdering AO, RValue UpdateRVal,
+                               bool IsVolatile);
+  /// Emit atomic update as LLVM instructions.
+  void EmitAtomicUpdateOp(llvm::AtomicOrdering AO, RValue UpdateRal,
                           bool IsVolatile);
-
-    /// Materialize an atomic r-value in atomic-layout memory.
-    Address materializeRValue(RValue rvalue) const;
-
-    /// Creates temp alloca for intermediate operations on atomic value.
-    Address CreateTempAlloca() const;
-  private:
-    bool requiresMemSetZero(llvm::Type *type) const;
-
-
-    /// Emits atomic load as a libcall.
-    void EmitAtomicLoadLibcall(llvm::Value *AddForLoaded,
-                               llvm::AtomicOrdering AO, bool IsVolatile);
-    /// Emits atomic load as LLVM instruction.
-    llvm::Value *EmitAtomicLoadOp(llvm::AtomicOrdering AO, bool IsVolatile,
-                                  bool CmpXchg = false);
-    /// Emits atomic compare-and-exchange op as a libcall.
-    llvm::Value *EmitAtomicCompareExchangeLibcall(
-        llvm::Value *ExpectedAddr, llvm::Value *DesiredAddr,
-        llvm::AtomicOrdering Success =
-            llvm::AtomicOrdering::SequentiallyConsistent,
-        llvm::AtomicOrdering Failure =
-            llvm::AtomicOrdering::SequentiallyConsistent);
-    /// Emits atomic compare-and-exchange op as LLVM instruction.
-    std::pair<llvm::Value *, llvm::Value *> EmitAtomicCompareExchangeOp(
-        llvm::Value *ExpectedVal, llvm::Value *DesiredVal,
-        llvm::AtomicOrdering Success =
-            llvm::AtomicOrdering::SequentiallyConsistent,
-        llvm::AtomicOrdering Failure =
-            llvm::AtomicOrdering::SequentiallyConsistent,
-        bool IsWeak = false);
-    /// Emit atomic update as libcalls.
-    void
-    EmitAtomicUpdateLibcall(llvm::AtomicOrdering AO,
-                            const llvm::function_ref<RValue(RValue)> &UpdateOp,
-                            bool IsVolatile);
-    /// Emit atomic update as LLVM instructions.
-    void EmitAtomicUpdateOp(llvm::AtomicOrdering AO,
-                            const llvm::function_ref<RValue(RValue)> &UpdateOp,
-                            bool IsVolatile);
-    /// Emit atomic update as libcalls.
-    void EmitAtomicUpdateLibcall(llvm::AtomicOrdering AO, RValue UpdateRVal,
-                                 bool IsVolatile);
-    /// Emit atomic update as LLVM instructions.
-    void EmitAtomicUpdateOp(llvm::AtomicOrdering AO, RValue UpdateRal,
-                            bool IsVolatile);
-  };
+};
 }
 
 Address AtomicInfo::CreateTempAlloca() const {
@@ -1002,7 +1050,7 @@ RValue CodeGenFunction::EmitAtomicExpr(AtomicExpr *E) {
   // need to make sure (via temporaries if necessary) that all incoming values
   // are compatible.
   LValue AtomicVal = MakeAddrLValue(Ptr, AtomicTy);
-  AtomicInfo Atomics(*this, AtomicVal);
+  AtomicInfo Atomics = AtomicInfo::fromValue(*this, AtomicVal);
 
   if (ShouldCastToIntPtrTy) {
     Ptr = Atomics.castToAtomicIntPointer(Ptr);
@@ -1401,17 +1449,6 @@ RValue AtomicInfo::convertAtomicTempToRValue(Address addr,
       LVal.getBaseInfo(), TBAAAccessInfo()));
 }
 
-/// Return true if \param ValTy is a type that should be casted to integer
-/// around the atomic memory operation. If \param CmpXchg is true, then the
-/// cast of a floating point type is made as that instruction can not have
-/// floating point operands.  TODO: Allow compare-and-exchange and FP - see
-/// comment in AtomicExpandPass.cpp.
-static bool shouldCastToInt(llvm::Type *ValTy, bool CmpXchg) {
-  if (ValTy->isFloatingPointTy())
-    return ValTy->isX86_FP80Ty() || CmpXchg;
-  return !ValTy->isIntegerTy() && !ValTy->isPointerTy();
-}
-
 RValue AtomicInfo::ConvertToValueOrAtomic(llvm::Value *Val,
                                           AggValueSlot ResultSlot,
                                           SourceLocation Loc, bool AsValue,
@@ -1470,28 +1507,12 @@ void AtomicInfo::EmitAtomicLoadLibcall(llvm::Value *AddForLoaded,
   emitAtomicLibcall(CGF, "__atomic_load", CGF.getContext().VoidTy, Args);
 }
 
-llvm::Value *AtomicInfo::EmitAtomicLoadOp(llvm::AtomicOrdering AO,
-                                          bool IsVolatile, bool CmpXchg) {
-  // Okay, we're doing this natively.
-  Address Addr = getAtomicAddress();
-  if (shouldCastToInt(Addr.getElementType(), CmpXchg))
-    Addr = castToAtomicIntPointer(Addr);
-  llvm::LoadInst *Load = CGF.Builder.CreateLoad(Addr, "atomic-load");
-  Load->setAtomic(AO);
-
-  // Other decoration.
-  if (IsVolatile)
-    Load->setVolatile(true);
-  CGF.CGM.DecorateInstructionWithTBAA(Load, LVal.getTBAAInfo());
-  return Load;
-}
-
 /// An LValue is a candidate for having its loads and stores be made atomic if
 /// we are operating under /volatile:ms *and* the LValue itself is volatile and
 /// performing such an operation can be performed without a libcall.
 bool CodeGenFunction::LValueIsSuitableForInlineAtomic(LValue LV) {
   if (!CGM.getLangOpts().MSVolatile) return false;
-  AtomicInfo AI(*this, LV);
+  AtomicInfo AI = AtomicInfo::fromValue(*this, LV);
   bool IsVolatile = LV.isVolatile() || hasVolatileMember(LV.getType());
   // An atomic is inline if we don't need to use a libcall.
   bool AtomicIsInline = !AI.shouldUseLibcall();
@@ -1551,9 +1572,8 @@ RValue AtomicInfo::EmitAtomicLoad(AggValueSlot ResultSlot, SourceLocation Loc,
 RValue CodeGenFunction::EmitAtomicLoad(LValue src, SourceLocation loc,
                                        llvm::AtomicOrdering AO, bool IsVolatile,
                                        AggValueSlot resultSlot) {
-  AtomicInfo Atomics(*this, src);
-  return Atomics.EmitAtomicLoad(resultSlot, loc, /*AsValue=*/true, AO,
-                                IsVolatile);
+  return AtomicInfo::fromValue(*this, src)
+      .EmitAtomicLoad(resultSlot, loc, /*AsValue=*/true, AO, IsVolatile);
 }
 
 /// Copy an r-value into memory as part of storing to an atomic type.
@@ -1601,8 +1621,7 @@ Address AtomicInfo::materializeRValue(RValue rvalue) const {
 
   // Otherwise, make a temporary and materialize into it.
   LValue TempLV = CGF.MakeAddrLValue(CreateTempAlloca(), getAtomicType());
-  AtomicInfo Atomics(CGF, TempLV);
-  Atomics.emitCopyIntoMemory(rvalue);
+  AtomicInfo::fromValue(CGF, TempLV).emitCopyIntoMemory(rvalue);
   return TempLV.getAddress();
 }
 
@@ -1635,70 +1654,18 @@ llvm::Value *AtomicInfo::convertRValueToInt(RValue RVal, bool CmpXchg) const {
   return CGF.Builder.CreateLoad(Addr);
 }
 
-std::pair<llvm::Value *, llvm::Value *> AtomicInfo::EmitAtomicCompareExchangeOp(
-    llvm::Value *ExpectedVal, llvm::Value *DesiredVal,
-    llvm::AtomicOrdering Success, llvm::AtomicOrdering Failure, bool IsWeak) {
-  // Do the atomic store.
-  Address Addr = getAtomicAddressAsAtomicIntPointer();
-  auto *Inst = CGF.Builder.CreateAtomicCmpXchg(Addr, ExpectedVal, DesiredVal,
-                                               Success, Failure);
-  // Other decoration.
-  Inst->setVolatile(LVal.isVolatileQualified());
-  Inst->setWeak(IsWeak);
-
-  // Okay, turn that back into the original value type.
-  auto *PreviousVal = CGF.Builder.CreateExtractValue(Inst, /*Idxs=*/0);
-  auto *SuccessFailureVal = CGF.Builder.CreateExtractValue(Inst, /*Idxs=*/1);
-  return std::make_pair(PreviousVal, SuccessFailureVal);
-}
-
-llvm::Value *
-AtomicInfo::EmitAtomicCompareExchangeLibcall(llvm::Value *ExpectedAddr,
-                                             llvm::Value *DesiredAddr,
-                                             llvm::AtomicOrdering Success,
-                                             llvm::AtomicOrdering Failure) {
-  // bool __atomic_compare_exchange(size_t size, void *obj, void *expected,
-  // void *desired, int success, int failure);
-  CallArgList Args;
-  Args.add(RValue::get(getAtomicSizeValue()), CGF.getContext().getSizeType());
-  Args.add(RValue::get(getAtomicPointer()), CGF.getContext().VoidPtrTy);
-  Args.add(RValue::get(ExpectedAddr), CGF.getContext().VoidPtrTy);
-  Args.add(RValue::get(DesiredAddr), CGF.getContext().VoidPtrTy);
-  Args.add(RValue::get(
-               llvm::ConstantInt::get(CGF.IntTy, (int)llvm::toCABI(Success))),
-           CGF.getContext().IntTy);
-  Args.add(RValue::get(
-               llvm::ConstantInt::get(CGF.IntTy, (int)llvm::toCABI(Failure))),
-           CGF.getContext().IntTy);
-  auto SuccessFailureRVal = emitAtomicLibcall(CGF, "__atomic_compare_exchange",
-                                              CGF.getContext().BoolTy, Args);
-
-  return SuccessFailureRVal.getScalarVal();
-}
-
 std::pair<RValue, llvm::Value *> AtomicInfo::EmitAtomicCompareExchange(
     RValue Expected, RValue Desired, llvm::AtomicOrdering Success,
     llvm::AtomicOrdering Failure, bool IsWeak) {
-  // Check whether we should use a library call.
-  if (shouldUseLibcall()) {
-    // Produce a source address.
-    Address ExpectedAddr = materializeRValue(Expected);
-    llvm::Value *ExpectedPtr = ExpectedAddr.emitRawPointer(CGF);
-    llvm::Value *DesiredPtr = materializeRValue(Desired).emitRawPointer(CGF);
-    auto *Res = EmitAtomicCompareExchangeLibcall(ExpectedPtr, DesiredPtr,
-                                                 Success, Failure);
-    return std::make_pair(
-        convertAtomicTempToRValue(ExpectedAddr, AggValueSlot::ignored(),
-                                  SourceLocation(), /*AsValue=*/false),
-        Res);
-  }
 
-  // If we've got a scalar value of the right size, try to avoid going
-  // through memory.
-  auto *ExpectedVal = convertRValueToInt(Expected, /*CmpXchg=*/true);
+  Address ExpectedAddr = materializeRValue(Expected);
+  llvm::Value *ExpectedPtr = ExpectedAddr.emitRawPointer(CGF);
   auto *DesiredVal = convertRValueToInt(Desired, /*CmpXchg=*/true);
-  auto Res = EmitAtomicCompareExchangeOp(ExpectedVal, DesiredVal, Success,
-                                         Failure, IsWeak);
+
+  std::pair<llvm::Value *, llvm::Value *> Res =
+      getBase().EmitAtomicCompareExchange(ExpectedPtr, DesiredVal, Success,
+                                          Failure, LVal.isVolatileQualified(),
+                                          IsWeak);
   return std::make_pair(
       ConvertToValueOrAtomic(Res.first, AggValueSlot::ignored(),
                              SourceLocation(), /*AsValue=*/false,
@@ -1784,9 +1751,9 @@ void AtomicInfo::EmitAtomicUpdateLibcall(
   EmitAtomicUpdateValue(CGF, *this, OldRVal, UpdateOp, DesiredAddr);
   llvm::Value *ExpectedPtr = ExpectedAddr.emitRawPointer(CGF);
   llvm::Value *DesiredPtr = DesiredAddr.emitRawPointer(CGF);
-  auto *Res =
+  auto Res =
       EmitAtomicCompareExchangeLibcall(ExpectedPtr, DesiredPtr, AO, Failure);
-  CGF.Builder.CreateCondBr(Res, ExitBB, ContBB);
+  CGF.Builder.CreateCondBr(Res.second, ExitBB, ContBB);
   CGF.EmitBlock(ExitBB, /*IsFinished=*/true);
 }
 
@@ -1872,9 +1839,9 @@ void AtomicInfo::EmitAtomicUpdateLibcall(llvm::AtomicOrdering AO,
   EmitAtomicUpdateValue(CGF, *this, UpdateRVal, DesiredAddr);
   llvm::Value *ExpectedPtr = ExpectedAddr.emitRawPointer(CGF);
   llvm::Value *DesiredPtr = DesiredAddr.emitRawPointer(CGF);
-  auto *Res =
+  auto Res =
       EmitAtomicCompareExchangeLibcall(ExpectedPtr, DesiredPtr, AO, Failure);
-  CGF.Builder.CreateCondBr(Res, ExitBB, ContBB);
+  CGF.Builder.CreateCondBr(Res.second, ExitBB, ContBB);
   CGF.EmitBlock(ExitBB, /*IsFinished=*/true);
 }
 
@@ -1953,7 +1920,7 @@ void CodeGenFunction::EmitAtomicStore(RValue rvalue, LValue dest,
          rvalue.getAggregateAddress().getElementType() ==
              dest.getAddress().getElementType());
 
-  AtomicInfo atomics(*this, dest);
+  AtomicInfo atomics = AtomicInfo ::fromValue(*this, dest);
   LValue LVal = atomics.getAtomicLValue();
 
   // If this is an initialization, just put the value there normally.
@@ -1988,7 +1955,8 @@ void CodeGenFunction::EmitAtomicStore(RValue rvalue, LValue dest,
     // Do the atomic store.
     Address Addr = atomics.getAtomicAddress();
     if (llvm::Value *Value = atomics.getScalarRValValueOrNull(rvalue))
-      if (shouldCastToInt(Value->getType(), /*CmpXchg=*/false)) {
+      if (llvm::AtomicInfo<CGBuilderTy>::shouldCastToInt(Value->getType(),
+                                                         /*CmpXchg=*/false)) {
         Addr = atomics.castToAtomicIntPointer(Addr);
         ValToStore = Builder.CreateIntCast(ValToStore, Addr.getElementType(),
                                            /*isSigned=*/false);
@@ -2028,21 +1996,18 @@ std::pair<RValue, llvm::Value *> CodeGenFunction::EmitAtomicCompareExchange(
   assert(!Desired.isAggregate() ||
          Desired.getAggregateAddress().getElementType() ==
              Obj.getAddress().getElementType());
-  AtomicInfo Atomics(*this, Obj);
-
-  return Atomics.EmitAtomicCompareExchange(Expected, Desired, Success, Failure,
-                                           IsWeak);
+  return AtomicInfo::fromValue(*this, Obj)
+      .EmitAtomicCompareExchange(Expected, Desired, Success, Failure, IsWeak);
 }
 
 void CodeGenFunction::EmitAtomicUpdate(
     LValue LVal, llvm::AtomicOrdering AO,
     const llvm::function_ref<RValue(RValue)> &UpdateOp, bool IsVolatile) {
-  AtomicInfo Atomics(*this, LVal);
-  Atomics.EmitAtomicUpdate(AO, UpdateOp, IsVolatile);
+  AtomicInfo::fromValue(*this, LVal).EmitAtomicUpdate(AO, UpdateOp, IsVolatile);
 }
 
 void CodeGenFunction::EmitAtomicInit(Expr *init, LValue dest) {
-  AtomicInfo atomics(*this, dest);
+  AtomicInfo atomics = AtomicInfo::fromValue(*this, dest);
 
   switch (atomics.getEvaluationKind()) {
   case TEK_Scalar: {
diff --git a/flang/test/Integration/OpenMP/atomic-update-complex.f90 b/flang/test/Integration/OpenMP/atomic-update-complex.f90
new file mode 100644
index 0000000000000..6e0b419d11f95
--- /dev/null
+++ b/flang/test/Integration/OpenMP/atomic-update-complex.f90
@@ -0,0 +1,42 @@
+!===----------------------------------------------------------------------===!
+! This directory can be used to add Integration tests involving multiple
+! stages of the compiler (for eg. from Fortran to LLVM IR). It should not
+! contain executable tests. We should only add tests here sparingly and only
+! if there is no other way to test. Repeat this message in each test that is
+! added to this directory and sub-directories.
+!===----------------------------------------------------------------------===!
+
+!RUN: %flang_fc1 -emit-llvm -fopenmp %s -o - | FileCheck %s
+
+!CHECK: define void @_QQmain() {
+!CHECK: %[[X_NEW_VAL:.*]] = alloca { float, float }, align 8
+!CHECK: {{.*}} = alloca { float, float }, i64 1, align 8
+!CHECK: %[[ORIG_VAL:.*]] = alloca { float, float }, i64 1, align 8
+!CHECK: store { float, float } { float 2.000000e+00, float 2.000000e+00 }, ptr %[[ORIG_VAL]], align 4
+!CHECK: br label %entry
+
+!CHECK: entry:
+!CHECK: %[[ATOMIC_TEMP_LOAD:.*]] = alloca { float, float }, align 16
+!CHECK: call void @__atomic_load(i64 8, ptr %[[ORIG_VAL]], ptr %[[ATOMIC_TEMP_LOAD]], i32 0)
+!CHECK: %[[PHI_NODE_ENTRY_1:.*]] = load { float, float }, ptr %[[ATOMIC_TEMP_LOAD]], align 16
+!CHECK: br label %.atomic.cont
+
+!CHECK: .atomic.cont
+!CHECK: %[[VAL_4:.*]] = phi { float, float } [ %[[PHI_NODE_ENTRY_1]], %entry ], [ %{{.*}}, %.atomic.cont ]
+!CHECK: %[[VAL_5:.*]] = extractvalue { float, float } %[[VAL_4]], 0
+!CHECK: %[[VAL_6:.*]] = extractvalue { float, float } %[[VAL_4]], 1
+!CHECK: %[[VAL_7:.*]] = fadd contract float %[[VAL_5]], 1.000000e+00
+!CHECK: %[[VAL_8:.*]] = fadd contract float %[[VAL_6]], 1.000000e+00
+!CHECK: %[[VAL_9:.*]] = insertvalue { float, float } undef, float %[[VAL_7]], 0
+!CHECK: %[[VAL_10:.*]] = insertvalue { float, float } %[[VAL_9]], float %[[VAL_8]], 1
+!CHECK: store { float, float } %[[VAL_10]], ptr %[[X_NEW_VAL]], align 4
+!CHECK: %[[VAL_11:.*]] = call i1 @__atomic_compare_exchange(i64 8, ptr %[[ORIG_VAL]], ptr %[[ATOMIC_TEMP_LOAD]], ptr %[[X_NEW_VAL]], i32 2, i32 2)
+!CHECK: %[[VAL_12:.*]] = load { float, float }, ptr %[[ATOMIC_TEMP_LOAD]], align 4
+!CHECK: br i1 %[[VAL_11]], label %.atomic.exit, label %.atomic.cont
+program main
+      complex*8 ia, ib
+      ia = (2, 2)
+      !$omp atomic update
+        ia = ia + (1, 1)
+      !$omp end atomic
+end program
diff --git a/llvm/include/llvm/Frontend/Atomic/Atomic.h b/llvm/include/llvm/Frontend/Atomic/Atomic.h
new file mode 100644
index 0000000000000..05296ef44c90f
--- /dev/null
+++ b/llvm/include/llvm/Frontend/Atomic/Atomic.h
@@ -0,0 +1,235 @@
+//===--- Atomic.h - Shared codegen of atomic operations -------------------===//
+//
+// 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 LLVM_FRONTEND_ATOMIC_ATOMIC_H
+#define LLVM_FRONTEND_ATOMIC_ATOMIC_H
+
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/CodeGen/RuntimeLibcalls.h"
+#include "llvm/IR/DataLayout.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/Intrinsics.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/Operator.h"
+
+namespace llvm {
+
+template <typename IRBuilderTy> struct AtomicInfo {
+  IRBuilderTy *Builder;
+
+  Type *Ty;
+  uint64_t AtomicSizeInBits;
+  uint64_t ValueSizeInBits;
+  llvm::Align AtomicAlign;
+  llvm::Align ValueAlign;
+  bool UseLibcall;
+
+public:
+  AtomicInfo(IRBuilderTy *Builder, Type *Ty, uint64_t AtomicSizeInBits,
+             uint64_t ValueSizeInBits, llvm::Align AtomicAlign,
+             llvm::Align ValueAlign, bool UseLibcall)
+      : Builder(Builder), Ty(Ty), AtomicSizeInBits(AtomicSizeInBits),
+        ValueSizeInBits(ValueSizeInBits), AtomicAlign(AtomicAlign),
+        ValueAlign(ValueAlign), UseLibcall(UseLibcall) {}
+
+  virtual ~AtomicInfo() = default;
+
+  llvm::Align getAtomicAlignment() const { return AtomicAlign; }
+  uint64_t getAtomicSizeInBits() const { return AtomicSizeInBits; }
+  uint64_t getValueSizeInBits() const { return ValueSizeInBits; }
+  bool shouldUseLibcall() const { return UseLibcall; }
+  llvm::Type *getAtomicTy() const { return Ty; }
+
+  virtual llvm::Value *getAtomicPointer() const = 0;
+  virtual void decorateWithTBAA(Instruction *I) {}
+  virtual llvm::AllocaInst *CreateAlloca(llvm::Type *Ty,
+                                         const llvm::Twine &Name) = 0;
+
+  /// Is the atomic size larger than the underlying value type?
+  ///
+  /// Note that the absence of padding does not mean that atomic
+  /// objects are completely interchangeable with non-atomic
+  /// objects: we might have promoted the alignment of a type
+  /// without making it bigger.
+  bool hasPadding() const { return (ValueSizeInBits != AtomicSizeInBits); }
+
+  LLVMContext &getLLVMContext() const { return Builder->getContext(); }
+
+  static bool shouldCastToInt(llvm::Type *ValTy, bool CmpXchg) {
+    if (ValTy->isFloatingPointTy())
+      return ValTy->isX86_FP80Ty() || CmpXchg;
+    return !ValTy->isIntegerTy() && !ValTy->isPointerTy();
+  }
+
+  llvm::Value *EmitAtomicLoadOp(llvm::AtomicOrdering AO, bool IsVolatile,
+                                bool CmpXchg = false) {
+    Value *Ptr = getAtomicPointer();
+    Type *AtomicTy = Ty;
+    if (shouldCastToInt(Ty, CmpXchg))
+      AtomicTy = llvm::IntegerType::get(getLLVMContext(), AtomicSizeInBits);
+    LoadInst *Load =
+        Builder->CreateAlignedLoad(AtomicTy, Ptr, AtomicAlign, "atomic-load");
+    Load->setAtomic(AO);
+
+    // Other decoration.
+    if (IsVolatile)
+      Load->setVolatile(true);
+    decorateWithTBAA(Load);
+    return Load;
+  }
+
+  static CallInst *emitAtomicLibcalls2(IRBuilderTy *Builder, StringRef fnName,
+                                       Type *ResultType,
+                                       ArrayRef<Value *> Args) {
+    auto &C = Builder->getContext();
+
+    SmallVector<Type *, 6> ArgTys;
+    for (Value *Arg : Args)
+      ArgTys.push_back(Arg->getType());
+    FunctionType *FnType = FunctionType::get(ResultType, ArgTys, false);
+    Module *M = Builder->GetInsertBlock()->getModule();
+
+    // TODO: Use llvm::TargetLowering for Libcall ABI
+    llvm::AttrBuilder fnAttrB(C);
+    fnAttrB.addAttribute(llvm::Attribute::NoUnwind);
+    fnAttrB.addAttribute(llvm::Attribute::WillReturn);
+    llvm::AttributeList fnAttrs = llvm::AttributeList::get(
+        C, llvm::AttributeList::FunctionIndex, fnAttrB);
+    FunctionCallee LibcallFn = M->getOrInsertFunction(fnName, FnType, fnAttrs);
+    CallInst *Call = Builder->CreateCall(LibcallFn, Args);
+
+    return Call;
+  }
+
+  llvm::Value *getAtomicSizeValue() const {
+    auto &C = getLLVMContext();
+
+    // TODO: Get from llvm::TargetMachine / clang::TargetInfo
+    uint16_t SizeTBits = 64;
+    uint16_t BitsPerByte = 8;
+    return llvm::ConstantInt::get(llvm::IntegerType::get(C, SizeTBits),
+                                  AtomicSizeInBits / BitsPerByte);
+  }
+
+  std::pair<llvm::Value *, llvm::Value *> EmitAtomicCompareExchangeLibcall(
+      llvm::Value *ExpectedVal, llvm::Value *DesiredVal,
+      llvm::AtomicOrdering Success, llvm::AtomicOrdering Failure) {
+    auto &C = getLLVMContext();
+
+    // __atomic_compare_exchange's expected and desired are passed by pointers
+    // FIXME: types
+
+    // TODO: Get from llvm::TargetMachine / clang::TargetInfo
+    uint64_t IntBits = 32;
+
+    // bool __atomic_compare_exchange(size_t size, void *obj, void *expected,
+    // void *desired, int success, int failure);
+    llvm::Value *Args[6] = {
+        getAtomicSizeValue(),
+        getAtomicPointer(),
+        ExpectedVal,
+        DesiredVal,
+        llvm::Constant::getIntegerValue(
+            llvm::IntegerType::get(C, IntBits),
+            llvm::APInt(IntBits, (uint64_t)Success, /*signed=*/true)),
+        llvm::Constant::getIntegerValue(
+            llvm::IntegerType::get(C, IntBits),
+            llvm::APInt(IntBits, (uint64_t)Failure, /*signed=*/true)),
+    };
+    auto Res = emitAtomicLibcalls2(Builder, "__atomic_compare_exchange",
+                                   llvm::IntegerType::getInt1Ty(C), Args);
+    return std::make_pair(ExpectedVal, Res);
+  }
+
+  Value *castToAtomicIntPointer(Value *addr) const {
+    return addr; // opaque pointer
+  }
+
+  Value *getAtomicAddressAsAtomicIntPointer() const {
+    return castToAtomicIntPointer(getAtomicPointer());
+  }
+
+  std::pair<llvm::Value *, llvm::Value *>
+  EmitAtomicCompareExchangeOp(llvm::Value *ExpectedVal, llvm::Value *DesiredVal,
+                              llvm::AtomicOrdering Success,
+                              llvm::AtomicOrdering Failure,
+                              bool IsVolatile = false, bool IsWeak = false) {
+    // Do the atomic store.
+    Value *Addr = getAtomicAddressAsAtomicIntPointer();
+    auto *Inst = Builder->CreateAtomicCmpXchg(Addr, ExpectedVal, DesiredVal,
+                                              getAtomicAlignment(), Success,
+                                              Failure, llvm::SyncScope::System);
+    // Other decoration.
+    Inst->setVolatile(IsVolatile);
+    Inst->setWeak(IsWeak);
+
+    // Okay, turn that back into the original value type.
+    auto *PreviousVal = Builder->CreateExtractValue(Inst, /*Idxs=*/0);
+    auto *SuccessFailureVal = Builder->CreateExtractValue(Inst, /*Idxs=*/1);
+    return std::make_pair(PreviousVal, SuccessFailureVal);
+  }
+
+  std::pair<llvm::Value *, llvm::Value *>
+  EmitAtomicCompareExchange(llvm::Value *ExpectedVal, llvm::Value *DesiredVal,
+                            llvm::AtomicOrdering Success,
+                            llvm::AtomicOrdering Failure, bool IsVolatile,
+                            bool IsWeak) {
+
+    // Check whether we should use a library call.
+    if (shouldUseLibcall()) {
+      return EmitAtomicCompareExchangeLibcall(ExpectedVal, DesiredVal, Success,
+                                              Failure);
+    }
+
+    // If we've got a scalar value of the right size, try to avoid going
+    // through memory.
+    auto Res = EmitAtomicCompareExchangeOp(ExpectedVal, DesiredVal, Success,
+                                           Failure, IsVolatile, IsWeak);
+    return Res;
+  }
+
+  // void __atomic_load(size_t size, void *mem, void *return, int order);
+  std::pair<llvm::LoadInst *, llvm::AllocaInst *>
+  EmitAtomicLoadLibcall(llvm::AtomicOrdering AO) {
+    LLVMContext &Ctx = getLLVMContext();
+    Type *SizedIntTy = Type::getIntNTy(Ctx, getAtomicSizeInBits() * 8);
+    Type *ResultTy;
+    SmallVector<Value *, 6> Args;
+    AttributeList Attr;
+    Module *M = Builder->GetInsertBlock()->getModule();
+    const DataLayout &DL = M->getDataLayout();
+    Args.push_back(ConstantInt::get(DL.getIntPtrType(Ctx),
+                                    this->getAtomicSizeInBits() / 8));
+    Value *PtrVal = getAtomicPointer();
+    PtrVal = Builder->CreateAddrSpaceCast(PtrVal, PointerType::getUnqual(Ctx));
+    Args.push_back(PtrVal);
+    AllocaInst *AllocaResult =
+        CreateAlloca(Ty, getAtomicPointer()->getName() + "atomic.temp.load");
+    const Align AllocaAlignment = DL.getPrefTypeAlign(SizedIntTy);
+    AllocaResult->setAlignment(AllocaAlignment);
+    Args.push_back(AllocaResult);
+    Constant *OrderingVal =
+        ConstantInt::get(Type::getInt32Ty(Ctx), (int)toCABI(AO));
+    Args.push_back(OrderingVal);
+    ResultTy = Type::getVoidTy(Ctx);
+    SmallVector<Type *, 6> ArgTys;
+    for (Value *Arg : Args)
+      ArgTys.push_back(Arg->getType());
+    FunctionType *FnType = FunctionType::get(ResultTy, ArgTys, false);
+    FunctionCallee LibcallFn =
+        M->getOrInsertFunction("__atomic_load", FnType, Attr);
+    CallInst *Call = Builder->CreateCall(LibcallFn, Args);
+    Call->setAttributes(Attr);
+    return std::make_pair(
+        Builder->CreateAlignedLoad(Ty, AllocaResult, AllocaAlignment),
+        AllocaResult);
+  }
+};
+} // end namespace llvm
+
+#endif /* LLVM_FRONTEND_ATOMIC_ATOMIC_H */
diff --git a/llvm/include/llvm/Frontend/OpenMP/OMPIRBuilder.h b/llvm/include/llvm/Frontend/OpenMP/OMPIRBuilder.h
index bff49dab4a313..badd6740acf9a 100644
--- a/llvm/include/llvm/Frontend/OpenMP/OMPIRBuilder.h
+++ b/llvm/include/llvm/Frontend/OpenMP/OMPIRBuilder.h
@@ -15,6 +15,7 @@
 #define LLVM_FRONTEND_OPENMP_OMPIRBUILDER_H
 
 #include "llvm/Analysis/MemorySSAUpdater.h"
+#include "llvm/Frontend/Atomic/Atomic.h"
 #include "llvm/Frontend/OpenMP/OMPConstants.h"
 #include "llvm/IR/DebugLoc.h"
 #include "llvm/IR/IRBuilder.h"
@@ -457,6 +458,29 @@ class OpenMPIRBuilder {
         T(Triple(M.getTargetTriple())) {}
   ~OpenMPIRBuilder();
 
+  class AtomicInfo : public llvm::AtomicInfo<IRBuilder<>> {
+
+    llvm::Value *AtomicVar;
+
+  public:
+    AtomicInfo(IRBuilder<> *Builder, llvm::Type *Ty, uint64_t AtomicSizeInBits,
+               uint64_t ValueSizeInBits, llvm::Align AtomicAlign,
+               llvm::Align ValueAlign, bool UseLibcall, llvm::Value *AtomicVar)
+        : llvm::AtomicInfo<IRBuilder<>>(Builder, Ty, AtomicSizeInBits,
+                                        ValueSizeInBits, AtomicAlign,
+                                        ValueAlign, UseLibcall),
+          AtomicVar(AtomicVar) {}
+
+    llvm::Value *getAtomicPointer() const override { return AtomicVar; }
+    void decorateWithTBAA(llvm::Instruction *I) override {}
+    llvm::AllocaInst *CreateAlloca(llvm::Type *Ty,
+                                   const llvm::Twine &Name) override {
+      llvm::AllocaInst *allocaInst = Builder->CreateAlloca(Ty);
+      allocaInst->setName(Name);
+      return allocaInst;
+    }
+  };
+
   /// Initialize the internal state, this will put structures types and
   /// potentially other helpers into the underlying module. Must be called
   /// before any other method and only once! This internal state includes types
diff --git a/llvm/lib/Frontend/Atomic/Atomic.cpp b/llvm/lib/Frontend/Atomic/Atomic.cpp
new file mode 100644
index 0000000000000..1b051b31a9234
--- /dev/null
+++ b/llvm/lib/Frontend/Atomic/Atomic.cpp
@@ -0,0 +1,18 @@
+//===--- Atomic.cpp - Shared codegen of atomic operations -----------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Frontend/Atomic/Atomic.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/Frontend/Atomic/Atomic.h"
+#include "llvm/IR/DataLayout.h"
+#include "llvm/IR/Intrinsics.h"
+#include "llvm/IR/Operator.h"
+
+namespace {} // namespace
+
+namespace llvm {} // end namespace llvm
diff --git a/llvm/lib/Frontend/Atomic/CMakeLists.txt b/llvm/lib/Frontend/Atomic/CMakeLists.txt
new file mode 100644
index 0000000000000..06f950e4bee4b
--- /dev/null
+++ b/llvm/lib/Frontend/Atomic/CMakeLists.txt
@@ -0,0 +1,15 @@
+add_llvm_component_library(LLVMFrontendAtomic
+  Atomic.cpp
+
+  ADDITIONAL_HEADER_DIRS
+  ${LLVM_MAIN_INCLUDE_DIR}/llvm/Frontend/Atomic
+
+  DEPENDS
+  LLVMAnalysis
+  LLVMTargetParser
+
+  LINK_COMPONENTS
+  Core
+  Support
+  Analysis
+  )
diff --git a/llvm/lib/Frontend/CMakeLists.txt b/llvm/lib/Frontend/CMakeLists.txt
index 62dd0da1e6c2d..b305ce7d771ce 100644
--- a/llvm/lib/Frontend/CMakeLists.txt
+++ b/llvm/lib/Frontend/CMakeLists.txt
@@ -1,3 +1,4 @@
+add_subdirectory(Atomic)
 add_subdirectory(Driver)
 add_subdirectory(HLSL)
 add_subdirectory(OpenACC)
diff --git a/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp b/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp
index ccec66fcb7bac..55018b89873c3 100644
--- a/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp
+++ b/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp
@@ -23,6 +23,7 @@
 #include "llvm/Analysis/ScalarEvolution.h"
 #include "llvm/Analysis/TargetLibraryInfo.h"
 #include "llvm/Bitcode/BitcodeReader.h"
+#include "llvm/Frontend/Atomic/Atomic.h"
 #include "llvm/Frontend/Offloading/Utility.h"
 #include "llvm/Frontend/OpenMP/OMPGridValues.h"
 #include "llvm/IR/Attributes.h"
@@ -6033,6 +6034,52 @@ std::pair<Value *, Value *> OpenMPIRBuilder::emitAtomicUpdate(
       Res.second = Res.first;
     else
       Res.second = emitRMWOpAsInstruction(Res.first, Expr, RMWOp);
+  } else if (RMWOp == llvm::AtomicRMWInst::BinOp::BAD_BINOP) {
+    LoadInst *OldVal =
+        Builder.CreateLoad(XElemTy, X, X->getName() + ".atomic.load");
+    OldVal->setAtomic(AO);
+    const DataLayout &LoadDL = OldVal->getModule()->getDataLayout();
+    unsigned LoadSize =
+        LoadDL.getTypeStoreSize(OldVal->getPointerOperand()->getType());
+    OpenMPIRBuilder::AtomicInfo atomicInfo(&Builder, XElemTy, LoadSize * 8,
+                                           LoadSize * 8, OldVal->getAlign(),
+                                           OldVal->getAlign(), true, X);
+    auto AtomicLoadRes = atomicInfo.EmitAtomicLoadLibcall(AO);
+    BasicBlock *CurBB = Builder.GetInsertBlock();
+    Instruction *CurBBTI = CurBB->getTerminator();
+    CurBBTI = CurBBTI ? CurBBTI : Builder.CreateUnreachable();
+    BasicBlock *ExitBB =
+        CurBB->splitBasicBlock(CurBBTI, X->getName() + ".atomic.exit");
+    BasicBlock *ContBB = CurBB->splitBasicBlock(CurBB->getTerminator(),
+                                                X->getName() + ".atomic.cont");
+    ContBB->getTerminator()->eraseFromParent();
+    Builder.restoreIP(AllocaIP);
+    AllocaInst *NewAtomicAddr = Builder.CreateAlloca(XElemTy);
+    NewAtomicAddr->setName(X->getName() + "x.new.val");
+    Builder.SetInsertPoint(ContBB);
+    llvm::PHINode *PHI = Builder.CreatePHI(OldVal->getType(), 2);
+    PHI->addIncoming(AtomicLoadRes.first, CurBB);
+    Value *OldExprVal = PHI;
+    Value *Upd = UpdateOp(OldExprVal, Builder);
+    Builder.CreateStore(Upd, NewAtomicAddr);
+    AtomicOrdering Failure =
+        llvm::AtomicCmpXchgInst::getStrongestFailureOrdering(AO);
+    auto Result = atomicInfo.EmitAtomicCompareExchangeLibcall(
+        AtomicLoadRes.second, NewAtomicAddr, AO, Failure);
+    LoadInst *PHILoad = Builder.CreateLoad(XElemTy, Result.first);
+    PHI->addIncoming(PHILoad, Builder.GetInsertBlock());
+    Builder.CreateCondBr(Result.second, ExitBB, ContBB);
+    OldVal->eraseFromParent();
+    Res.first = OldExprVal;
+    Res.second = Upd;
+
+    if (UnreachableInst *ExitTI =
+            dyn_cast<UnreachableInst>(ExitBB->getTerminator())) {
+      CurBBTI->eraseFromParent();
+      Builder.SetInsertPoint(ExitBB);
+    } else {
+      Builder.SetInsertPoint(ExitTI);
+    }
   } else {
     IntegerType *IntCastTy =
         IntegerType::get(M.getContext(), XElemTy->getScalarSizeInBits());



More information about the flang-commits mailing list