[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:35:29 PDT 2024
https://github.com/NimishMishra created https://github.com/llvm/llvm-project/pull/92364
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
>From be8ccd58f8119acab7e38a60806ed21992940178 Mon Sep 17 00:00:00 2001
From: Nimish Mishra <neelam.nimish at gmail.com>
Date: Thu, 16 May 2024 13:54:09 +0530
Subject: [PATCH 1/2] [OpenMPIRBuilder] Emit __atomic_load and
__atomic_compare_exchange libcalls for complex types in atomic update
---
.../llvm/Frontend/OpenMP/OMPIRBuilder.h | 20 +-
llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp | 237 +++++++++++++++++-
.../OpenMP/OpenMPToLLVMIRTranslation.cpp | 7 +-
3 files changed, 252 insertions(+), 12 deletions(-)
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;
}
>From 46e58a1e9b766bd64d75f234ab5d941cd68f8efe Mon Sep 17 00:00:00 2001
From: Nimish Mishra <neelam.nimish at gmail.com>
Date: Thu, 16 May 2024 13:56:29 +0530
Subject: [PATCH 2/2] Add integration test
---
.../OpenMP/atomic-update-complex.f90 | 59 +++++++++++++++++++
1 file changed, 59 insertions(+)
create mode 100644 flang/test/Integration/OpenMP/atomic-update-complex.f90
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
More information about the flang-commits
mailing list