[flang-commits] [flang] [llvm] [mlir] [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
Thu May 16 01:36:00 PDT 2024


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-mlir-openmp

Author: None (NimishMishra)

<details>
<summary>Changes</summary>

This patch adds functionality to emit relevant libcalls in case atomicrmw instruction can not be emitted (for instance, in case of complex types).

Fixes https://github.com/llvm/llvm-project/issues/83760

---
Full diff: https://github.com/llvm/llvm-project/pull/92364.diff


4 Files Affected:

- (added) flang/test/Integration/OpenMP/atomic-update-complex.f90 (+59) 
- (modified) llvm/include/llvm/Frontend/OpenMP/OMPIRBuilder.h (+16-4) 
- (modified) llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp (+233-4) 
- (modified) mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp (+3-4) 


``````````diff
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..7fe37673e7dce
--- /dev/null
+++ b/flang/test/Integration/OpenMP/atomic-update-complex.f90
@@ -0,0 +1,59 @@
+!===----------------------------------------------------------------------===!
+! 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: %[[VAL_1:.*]] = alloca { float, float }, align 8
+!CHECK: %[[VAL_2:.*]] = alloca { float, float }, align 8
+!CHECK: %[[VAL_3:.*]] = alloca { float, float }, align 8
+!CHECK: %[[X_NEW_VAL:.*]] = alloca { float, float }, align 8
+!CHECK: %[[VAL_4:.*]] = alloca { float, float }, i64 1, align 8
+!CHECK: %[[VAL_5:.*]] = alloca { float, float }, i64 1, align 8
+!CHECK: store { float, float } { float 2.000000e+00, float 2.000000e+00 }, ptr %[[VAL_4]], align 4
+!CHECK: br label %entry
+
+program main
+      complex*8 ia, ib
+      ia = (2, 2)
+
+!CHECK: entry:
+!CHECK: call void @llvm.lifetime.start.p0(i64 8, ptr %[[VAL_3]])
+!CHECK: call void @__atomic_load(i64 8, ptr %[[VAL_4]], ptr %[[VAL_3]], i32 0)
+!CHECK: %[[VAL_6:.*]] = load { float, float }, ptr %[[VAL_3]], align 8
+!CHECK: call void @llvm.lifetime.end.p0(i64 8, ptr %[[VAL_3]])
+!CHECK: br label %.atomic.cont
+
+
+!CHECK: .atomic.cont
+!CHECK: %[[VAL_7:.*]] = phi { float, float } [ %[[VAL_6]], %entry ], [ {{.*}}, %.atomic.cont ]
+!CHECK: %[[VAL_8:.*]] = extractvalue { float, float } %[[VAL_7]], 0
+!CHECK: %[[VAL_9:.*]] = extractvalue { float, float } %[[VAL_7]], 1
+!CHECK: %[[VAL_10:.*]] = fadd contract float %[[VAL_8]], 1.000000e+00
+!CHECK: %[[VAL_11:.*]] = fadd contract float %[[VAL_9]], 1.000000e+00
+!CHECK: %[[VAL_12:.*]] = insertvalue { float, float } undef, float %[[VAL_10]], 0
+!CHECK: %[[VAL_13:.*]] = insertvalue { float, float } %[[VAL_12]], float %[[VAL_11]], 1
+!CHECK: store { float, float } %[[VAL_13]], ptr %[[X_NEW_VAL]], align 4
+!CHECK: %[[VAL_14:.*]] = load { float, float }, ptr %[[X_NEW_VAL]], align 4
+!CHECK: call void @llvm.lifetime.start.p0(i64 8, ptr %[[VAL_1]])
+!CHECK: store { float, float } %[[VAL_7]], ptr %[[VAL_1]], align 8
+!CHECK: call void @llvm.lifetime.start.p0(i64 8, ptr %[[VAL_2]])
+!CHECK: store { float, float } %[[VAL_14]], ptr %[[VAL_2]], align 8
+!CHECK: %[[VAL_15:.*]] = call zeroext i1 @__atomic_compare_exchange(i64 8, ptr %[[VAL_4]], ptr %[[VAL_1]], ptr %[[VAL_2]], i32 0, i32 0)
+!CHECK: call void @llvm.lifetime.end.p0(i64 8, ptr %[[VAL_2]])
+!CHECK: %[[VAL_16:.*]] = load { float, float }, ptr %[[VAL_1]], align 8
+!CHECK: %[[VAL_17:.*]] = insertvalue { { float, float }, i1 } poison, { float, float } %[[VAL_16]], 0
+!CHECK: %[[VAL_18:.*]] = insertvalue { { float, float }, i1 } %[[VAL_17]], i1 %[[VAL_15]], 1
+!CHECK: %[[VAL_19:.*]] = extractvalue { { float, float }, i1 } %[[VAL_18]], 0
+!CHECK: %[[VAL_20:.*]] = extractvalue { { float, float }, i1 } %[[VAL_18]], 1
+!CHECK: br i1 %[[VAL_20]], label %.atomic.exit, label %.atomic.cont
+      !$omp atomic update
+        ia = ia + (1, 1)
+      !$omp end atomic  
+      print *, ia
+end program main
diff --git a/llvm/include/llvm/Frontend/OpenMP/OMPIRBuilder.h b/llvm/include/llvm/Frontend/OpenMP/OMPIRBuilder.h
index c9ee0c25194c2..8a2dd1e40f5c2 100644
--- a/llvm/include/llvm/Frontend/OpenMP/OMPIRBuilder.h
+++ b/llvm/include/llvm/Frontend/OpenMP/OMPIRBuilder.h
@@ -2418,14 +2418,25 @@ class OpenMPIRBuilder {
   /// \param IsXBinopExpr true if \a X is Left H.S. in Right H.S. part of the
   ///                     update expression, false otherwise.
   ///                     (e.g. true for X = X BinOp Expr)
-  ///
+  /// \param shouldEmitLibCall  true is atomicrmw cannot be emitted for \a X
   /// \returns A pair of the old value of X before the update, and the value
   ///          used for the update.
   std::pair<Value *, Value *>
   emitAtomicUpdate(InsertPointTy AllocaIP, Value *X, Type *XElemTy, Value *Expr,
                    AtomicOrdering AO, AtomicRMWInst::BinOp RMWOp,
                    AtomicUpdateCallbackTy &UpdateOp, bool VolatileX,
-                   bool IsXBinopExpr);
+                   bool IsXBinopExpr, bool shouldEmitLibCall = false);
+
+  bool emitAtomicCompareExchangeLibCall(
+      Instruction *I, unsigned Size, Align Alignment, Value *PointerOperand,
+      Value *ValueOperand, Value *CASExpected, AtomicOrdering Ordering,
+      AtomicOrdering Ordering2, llvm::PHINode *PHI, llvm::BasicBlock *ContBB,
+      llvm::BasicBlock *ExitBB);
+
+  bool emitAtomicLoadLibCall(Instruction *I, unsigned Size, Align Alignment,
+                             Value *PointerOperand, Value *ValueOperand,
+                             Value *CASExpected, AtomicOrdering Ordering,
+                             AtomicOrdering Ordering2, Value *&LoadedVal);
 
   /// Emit the binary op. described by \p RMWOp, using \p Src1 and \p Src2 .
   ///
@@ -2487,14 +2498,15 @@ class OpenMPIRBuilder {
   /// \param IsXBinopExpr true if \a X is Left H.S. in Right H.S. part of the
   ///                     update expression, false otherwise.
   ///	                    (e.g. true for X = X BinOp Expr)
-  ///
+  /// \param shouldEmitLibCall  true is atomicrmw cannot be emitted for \a X
   /// \return Insertion point after generated atomic update IR.
   InsertPointTy createAtomicUpdate(const LocationDescription &Loc,
                                    InsertPointTy AllocaIP, AtomicOpValue &X,
                                    Value *Expr, AtomicOrdering AO,
                                    AtomicRMWInst::BinOp RMWOp,
                                    AtomicUpdateCallbackTy &UpdateOp,
-                                   bool IsXBinopExpr);
+                                   bool IsXBinopExpr,
+                                   bool shouldEmitLibCall = false);
 
   /// Emit atomic update for constructs: --- Only Scalar data types
   /// V = X; X = X BinOp Expr ,
diff --git a/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp b/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp
index 42ea20919a5e1..42afaa8aed36c 100644
--- a/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp
+++ b/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp
@@ -5936,7 +5936,8 @@ OpenMPIRBuilder::createAtomicWrite(const LocationDescription &Loc,
 OpenMPIRBuilder::InsertPointTy OpenMPIRBuilder::createAtomicUpdate(
     const LocationDescription &Loc, InsertPointTy AllocaIP, AtomicOpValue &X,
     Value *Expr, AtomicOrdering AO, AtomicRMWInst::BinOp RMWOp,
-    AtomicUpdateCallbackTy &UpdateOp, bool IsXBinopExpr) {
+    AtomicUpdateCallbackTy &UpdateOp, bool IsXBinopExpr,
+    bool shouldEmitLibCall) {
   assert(!isConflictIP(Loc.IP, AllocaIP) && "IPs must not be ambiguous");
   if (!updateToLocation(Loc))
     return Loc.IP;
@@ -5955,7 +5956,7 @@ OpenMPIRBuilder::InsertPointTy OpenMPIRBuilder::createAtomicUpdate(
   });
 
   emitAtomicUpdate(AllocaIP, X.Var, X.ElemTy, Expr, AO, RMWOp, UpdateOp,
-                   X.IsVolatile, IsXBinopExpr);
+                   X.IsVolatile, IsXBinopExpr, shouldEmitLibCall);
   checkAndEmitFlushAfterAtomic(Loc, AO, AtomicKind::Update);
   return Builder.saveIP();
 }
@@ -5993,10 +5994,180 @@ Value *OpenMPIRBuilder::emitRMWOpAsInstruction(Value *Src1, Value *Src2,
   llvm_unreachable("Unsupported atomic update operation");
 }
 
+bool OpenMPIRBuilder::emitAtomicCompareExchangeLibCall(
+    Instruction *I, unsigned Size, Align Alignment, Value *PointerOperand,
+    Value *ValueOperand, Value *CASExpected, AtomicOrdering Ordering,
+    AtomicOrdering Ordering2, llvm::PHINode *PHI, llvm::BasicBlock *ContBB,
+    llvm::BasicBlock *ExitBB) {
+
+  LLVMContext &Ctx = I->getContext();
+  Module *M = I->getModule();
+  const DataLayout &DL = M->getDataLayout();
+  IRBuilder<> Builder(I);
+  IRBuilder<> AllocaBuilder(&I->getFunction()->getEntryBlock().front());
+
+  Type *SizedIntTy = Type::getIntNTy(Ctx, Size * 8);
+
+  const Align AllocaAlignment = DL.getPrefTypeAlign(SizedIntTy);
+
+  ConstantInt *SizeVal64 = ConstantInt::get(Type::getInt64Ty(Ctx), Size);
+  assert(Ordering != AtomicOrdering::NotAtomic && "expect atomic MO");
+  Constant *OrderingVal =
+      ConstantInt::get(Type::getInt32Ty(Ctx), (int)toCABI(Ordering));
+  Constant *Ordering2Val = nullptr;
+  if (CASExpected) {
+    assert(Ordering2 != AtomicOrdering::NotAtomic && "expect atomic MO");
+    Ordering2Val =
+        ConstantInt::get(Type::getInt32Ty(Ctx), (int)toCABI(Ordering2));
+  }
+
+  bool HasResult = I->getType() != Type::getVoidTy(Ctx);
+  AllocaInst *AllocaCASExpected = nullptr;
+  AllocaInst *AllocaValue = nullptr;
+  AllocaInst *AllocaResult = nullptr;
+
+  Type *ResultTy;
+  SmallVector<Value *, 6> Args;
+  AttributeList Attr;
+
+  Args.push_back(ConstantInt::get(DL.getIntPtrType(Ctx), Size));
+
+  Value *PtrVal = PointerOperand;
+  PtrVal = Builder.CreateAddrSpaceCast(PtrVal, PointerType::getUnqual(Ctx));
+  Args.push_back(PtrVal);
+
+  if (CASExpected) {
+    AllocaCASExpected = AllocaBuilder.CreateAlloca(CASExpected->getType());
+    AllocaCASExpected->setAlignment(AllocaAlignment);
+    Builder.CreateLifetimeStart(AllocaCASExpected, SizeVal64);
+    Builder.CreateAlignedStore(CASExpected, AllocaCASExpected, AllocaAlignment);
+    Args.push_back(AllocaCASExpected);
+  }
+
+  if (ValueOperand) {
+    AllocaValue = AllocaBuilder.CreateAlloca(ValueOperand->getType());
+    AllocaValue->setAlignment(AllocaAlignment);
+    Builder.CreateLifetimeStart(AllocaValue, SizeVal64);
+    Builder.CreateAlignedStore(ValueOperand, AllocaValue, AllocaAlignment);
+    Args.push_back(AllocaValue);
+  }
+
+  if (!CASExpected && HasResult) {
+    AllocaResult = AllocaBuilder.CreateAlloca(I->getType());
+    AllocaResult->setAlignment(AllocaAlignment);
+    Builder.CreateLifetimeStart(AllocaResult, SizeVal64);
+    Args.push_back(AllocaResult);
+  }
+
+  Args.push_back(OrderingVal);
+
+  if (Ordering2Val)
+    Args.push_back(Ordering2Val);
+
+  ResultTy = Type::getInt1Ty(Ctx);
+  Attr = Attr.addRetAttribute(Ctx, Attribute::ZExt);
+
+  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_compare_exchange", FnType, Attr);
+  CallInst *Call = Builder.CreateCall(LibcallFn, Args);
+  Call->setAttributes(Attr);
+  Value *Result = Call;
+
+  if (ValueOperand)
+    Builder.CreateLifetimeEnd(AllocaValue, SizeVal64);
+
+  Type *FinalResultTy = I->getType();
+  Value *V = PoisonValue::get(FinalResultTy);
+  Value *ExpectedOut = Builder.CreateAlignedLoad(
+      CASExpected->getType(), AllocaCASExpected, AllocaAlignment);
+  Builder.CreateLifetimeEnd(AllocaCASExpected, SizeVal64);
+  V = Builder.CreateInsertValue(V, ExpectedOut, 0);
+  V = Builder.CreateInsertValue(V, Result, 1);
+  I->replaceAllUsesWith(V);
+  Value *PreviousVal = Builder.CreateExtractValue(V, /*Idxs=*/0);
+  Value *SuccessFailureVal = Builder.CreateExtractValue(V, /*Idxs=*/1);
+  PHI->addIncoming(PreviousVal, Builder.GetInsertBlock());
+  Builder.CreateCondBr(SuccessFailureVal, ExitBB, ContBB);
+  return true;
+}
+
+bool OpenMPIRBuilder::emitAtomicLoadLibCall(
+    Instruction *I, unsigned Size, Align Alignment, Value *PointerOperand,
+    Value *ValueOperand, Value *CASExpected, AtomicOrdering Ordering,
+    AtomicOrdering Ordering2, Value *&LoadedVal) {
+
+  LLVMContext &Ctx = I->getContext();
+  Module *M = I->getModule();
+  const DataLayout &DL = M->getDataLayout();
+  IRBuilder<> Builder(I);
+  IRBuilder<> AllocaBuilder(&I->getFunction()->getEntryBlock().front());
+
+  Type *SizedIntTy = Type::getIntNTy(Ctx, Size * 8);
+
+  const Align AllocaAlignment = DL.getPrefTypeAlign(SizedIntTy);
+
+  ConstantInt *SizeVal64 = ConstantInt::get(Type::getInt64Ty(Ctx), Size);
+  assert(Ordering != AtomicOrdering::NotAtomic && "expect atomic MO");
+  Constant *OrderingVal =
+      ConstantInt::get(Type::getInt32Ty(Ctx), (int)toCABI(Ordering));
+  Constant *Ordering2Val = nullptr;
+
+  bool HasResult = I->getType() != Type::getVoidTy(Ctx);
+  AllocaInst *AllocaCASExpected = nullptr;
+  AllocaInst *AllocaValue = nullptr;
+  AllocaInst *AllocaResult = nullptr;
+
+  Type *ResultTy;
+  SmallVector<Value *, 6> Args;
+  AttributeList Attr;
+
+  Args.push_back(ConstantInt::get(DL.getIntPtrType(Ctx), Size));
+
+  Value *PtrVal = PointerOperand;
+  PtrVal = Builder.CreateAddrSpaceCast(PtrVal, PointerType::getUnqual(Ctx));
+  Args.push_back(PtrVal);
+
+  if (!CASExpected && HasResult) {
+    AllocaResult = AllocaBuilder.CreateAlloca(I->getType());
+    AllocaResult->setAlignment(AllocaAlignment);
+    Builder.CreateLifetimeStart(AllocaResult, SizeVal64);
+    Args.push_back(AllocaResult);
+  }
+
+  Args.push_back(OrderingVal);
+
+  if (Ordering2Val)
+    Args.push_back(Ordering2Val);
+
+  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);
+
+  LoadedVal =
+      Builder.CreateAlignedLoad(I->getType(), AllocaResult, AllocaAlignment);
+  Builder.CreateLifetimeEnd(AllocaResult, SizeVal64);
+  I->replaceAllUsesWith(LoadedVal);
+  return true;
+}
+
 std::pair<Value *, Value *> OpenMPIRBuilder::emitAtomicUpdate(
     InsertPointTy AllocaIP, Value *X, Type *XElemTy, Value *Expr,
     AtomicOrdering AO, AtomicRMWInst::BinOp RMWOp,
-    AtomicUpdateCallbackTy &UpdateOp, bool VolatileX, bool IsXBinopExpr) {
+    AtomicUpdateCallbackTy &UpdateOp, bool VolatileX, bool IsXBinopExpr,
+    bool shouldEmitLibCall) {
   // TODO: handle the case where XElemTy is not byte-sized or not a power of 2
   // or a complex datatype.
   bool emitRMWOp = false;
@@ -6018,7 +6189,7 @@ std::pair<Value *, Value *> OpenMPIRBuilder::emitAtomicUpdate(
   emitRMWOp &= XElemTy->isIntegerTy();
 
   std::pair<Value *, Value *> Res;
-  if (emitRMWOp) {
+  if (emitRMWOp && !shouldEmitLibCall) {
     Res.first = Builder.CreateAtomicRMW(RMWOp, X, Expr, llvm::MaybeAlign(), AO);
     // not needed except in case of postfix captures. Generate anyway for
     // consistency with the else part. Will be removed with any DCE pass.
@@ -6027,6 +6198,64 @@ std::pair<Value *, Value *> OpenMPIRBuilder::emitAtomicUpdate(
       Res.second = Res.first;
     else
       Res.second = emitRMWOpAsInstruction(Res.first, Expr, RMWOp);
+  } else if (shouldEmitLibCall) {
+    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());
+
+    Value *LoadedVal = nullptr;
+    emitAtomicLoadLibCall(OldVal, LoadSize, OldVal->getAlign(),
+                          OldVal->getPointerOperand(), nullptr, nullptr,
+                          OldVal->getOrdering(), AtomicOrdering::NotAtomic,
+                          LoadedVal);
+
+    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(LoadedVal, CurBB);
+    Value *OldExprVal = PHI;
+
+    Value *Upd = UpdateOp(OldExprVal, Builder);
+    Builder.CreateStore(Upd, NewAtomicAddr);
+    LoadInst *DesiredVal = Builder.CreateLoad(XElemTy, NewAtomicAddr);
+    AtomicOrdering Failure =
+        llvm::AtomicCmpXchgInst::getStrongestFailureOrdering(AO);
+    AtomicCmpXchgInst *Result = Builder.CreateAtomicCmpXchg(
+        X, PHI, DesiredVal, llvm::MaybeAlign(), AO, Failure);
+
+    const DataLayout &DL = Result->getModule()->getDataLayout();
+    unsigned Size = DL.getTypeStoreSize(Result->getCompareOperand()->getType());
+
+    emitAtomicCompareExchangeLibCall(
+        Result, Size, Result->getAlign(), Result->getPointerOperand(),
+        Result->getNewValOperand(), Result->getCompareOperand(),
+        Result->getSuccessOrdering(), Result->getFailureOrdering(), PHI, ContBB,
+        ExitBB);
+
+    Result->eraseFromParent();
+    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());
diff --git a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
index bfd7d65912bdb..9ea4d96de4f95 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
@@ -1621,7 +1621,7 @@ convertOmpAtomicUpdate(omp::AtomicUpdateOp &opInst,
 
   // Convert values and types.
   auto &innerOpList = opInst.getRegion().front().getOperations();
-  bool isRegionArgUsed{false}, isXBinopExpr{false};
+  bool isRegionArgUsed{false}, isXBinopExpr{false}, shouldEmitLibCall{false};
   llvm::AtomicRMWInst::BinOp binop;
   mlir::Value mlirExpr;
   // Find the binary update operation that uses the region argument
@@ -1640,8 +1640,7 @@ convertOmpAtomicUpdate(omp::AtomicUpdateOp &opInst,
     }
   }
   if (!isRegionArgUsed)
-    return opInst.emitError("no atomic update operation with region argument"
-                            " as operand found inside atomic.update region");
+    shouldEmitLibCall = true;
 
   llvm::Value *llvmExpr = moduleTranslation.lookupValue(mlirExpr);
   llvm::Value *llvmX = moduleTranslation.lookupValue(opInst.getX());
@@ -1679,7 +1678,7 @@ convertOmpAtomicUpdate(omp::AtomicUpdateOp &opInst,
   llvm::OpenMPIRBuilder::LocationDescription ompLoc(builder);
   builder.restoreIP(ompBuilder->createAtomicUpdate(
       ompLoc, allocaIP, llvmAtomicX, llvmExpr, atomicOrdering, binop, updateFn,
-      isXBinopExpr));
+      isXBinopExpr, shouldEmitLibCall));
   return updateGenStatus;
 }
 

``````````

</details>


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


More information about the flang-commits mailing list