1044eb2f6SDimitry Andric //===- AtomicExpandPass.cpp - Expand atomic instructions ------------------===//
267c32a98SDimitry Andric //
3e6d15924SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e6d15924SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5e6d15924SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
667c32a98SDimitry Andric //
767c32a98SDimitry Andric //===----------------------------------------------------------------------===//
867c32a98SDimitry Andric //
967c32a98SDimitry Andric // This file contains a pass (at IR level) to replace atomic instructions with
1001095a5dSDimitry Andric // __atomic_* library calls, or target specific instruction which implement the
1101095a5dSDimitry Andric // same semantics in a way which better fits the target backend. This can
1201095a5dSDimitry Andric // include the use of (intrinsic-based) load-linked/store-conditional loops,
1301095a5dSDimitry Andric // AtomicCmpXchg, or type coercions.
1467c32a98SDimitry Andric //
1567c32a98SDimitry Andric //===----------------------------------------------------------------------===//
1667c32a98SDimitry Andric
17044eb2f6SDimitry Andric #include "llvm/ADT/ArrayRef.h"
18145449b1SDimitry Andric #include "llvm/ADT/STLFunctionalExtras.h"
19044eb2f6SDimitry Andric #include "llvm/ADT/SmallVector.h"
20e3b55780SDimitry Andric #include "llvm/Analysis/InstSimplifyFolder.h"
21c0981da4SDimitry Andric #include "llvm/Analysis/OptimizationRemarkEmitter.h"
22ac9a064cSDimitry Andric #include "llvm/CodeGen/AtomicExpand.h"
23dd58ef01SDimitry Andric #include "llvm/CodeGen/AtomicExpandUtils.h"
24ac9a064cSDimitry Andric #include "llvm/CodeGen/RuntimeLibcallUtil.h"
25044eb2f6SDimitry Andric #include "llvm/CodeGen/TargetLowering.h"
26b5630dbaSDimitry Andric #include "llvm/CodeGen/TargetPassConfig.h"
27044eb2f6SDimitry Andric #include "llvm/CodeGen/TargetSubtargetInfo.h"
28044eb2f6SDimitry Andric #include "llvm/CodeGen/ValueTypes.h"
29044eb2f6SDimitry Andric #include "llvm/IR/Attributes.h"
30044eb2f6SDimitry Andric #include "llvm/IR/BasicBlock.h"
31044eb2f6SDimitry Andric #include "llvm/IR/Constant.h"
32044eb2f6SDimitry Andric #include "llvm/IR/Constants.h"
33044eb2f6SDimitry Andric #include "llvm/IR/DataLayout.h"
34044eb2f6SDimitry Andric #include "llvm/IR/DerivedTypes.h"
3567c32a98SDimitry Andric #include "llvm/IR/Function.h"
3667c32a98SDimitry Andric #include "llvm/IR/IRBuilder.h"
3767c32a98SDimitry Andric #include "llvm/IR/InstIterator.h"
38044eb2f6SDimitry Andric #include "llvm/IR/Instruction.h"
3967c32a98SDimitry Andric #include "llvm/IR/Instructions.h"
40ac9a064cSDimitry Andric #include "llvm/IR/MDBuilder.h"
41ac9a064cSDimitry Andric #include "llvm/IR/MemoryModelRelaxationAnnotations.h"
4267c32a98SDimitry Andric #include "llvm/IR/Module.h"
43044eb2f6SDimitry Andric #include "llvm/IR/Type.h"
44044eb2f6SDimitry Andric #include "llvm/IR/User.h"
45044eb2f6SDimitry Andric #include "llvm/IR/Value.h"
46706b4fc4SDimitry Andric #include "llvm/InitializePasses.h"
47044eb2f6SDimitry Andric #include "llvm/Pass.h"
48044eb2f6SDimitry Andric #include "llvm/Support/AtomicOrdering.h"
49044eb2f6SDimitry Andric #include "llvm/Support/Casting.h"
5067c32a98SDimitry Andric #include "llvm/Support/Debug.h"
51044eb2f6SDimitry Andric #include "llvm/Support/ErrorHandling.h"
52dd58ef01SDimitry Andric #include "llvm/Support/raw_ostream.h"
5367c32a98SDimitry Andric #include "llvm/Target/TargetMachine.h"
54145449b1SDimitry Andric #include "llvm/Transforms/Utils/LowerAtomic.h"
55044eb2f6SDimitry Andric #include <cassert>
56044eb2f6SDimitry Andric #include <cstdint>
57044eb2f6SDimitry Andric #include <iterator>
5867c32a98SDimitry Andric
5967c32a98SDimitry Andric using namespace llvm;
6067c32a98SDimitry Andric
6167c32a98SDimitry Andric #define DEBUG_TYPE "atomic-expand"
6267c32a98SDimitry Andric
6367c32a98SDimitry Andric namespace {
64044eb2f6SDimitry Andric
65ac9a064cSDimitry Andric class AtomicExpandImpl {
66044eb2f6SDimitry Andric const TargetLowering *TLI = nullptr;
67e3b55780SDimitry Andric const DataLayout *DL = nullptr;
68044eb2f6SDimitry Andric
6967c32a98SDimitry Andric private:
706b3f41edSDimitry Andric bool bracketInstWithFences(Instruction *I, AtomicOrdering Order);
71dd58ef01SDimitry Andric IntegerType *getCorrespondingIntegerType(Type *T, const DataLayout &DL);
72dd58ef01SDimitry Andric LoadInst *convertAtomicLoadToIntegerType(LoadInst *LI);
73dd58ef01SDimitry Andric bool tryExpandAtomicLoad(LoadInst *LI);
7467c32a98SDimitry Andric bool expandAtomicLoadToLL(LoadInst *LI);
7567c32a98SDimitry Andric bool expandAtomicLoadToCmpXchg(LoadInst *LI);
76dd58ef01SDimitry Andric StoreInst *convertAtomicStoreToIntegerType(StoreInst *SI);
77145449b1SDimitry Andric bool tryExpandAtomicStore(StoreInst *SI);
78145449b1SDimitry Andric void expandAtomicStore(StoreInst *SI);
795a5ac124SDimitry Andric bool tryExpandAtomicRMW(AtomicRMWInst *AI);
80344a3780SDimitry Andric AtomicRMWInst *convertAtomicXchgToIntegerType(AtomicRMWInst *RMWI);
8101095a5dSDimitry Andric Value *
82e3b55780SDimitry Andric insertRMWLLSCLoop(IRBuilderBase &Builder, Type *ResultTy, Value *Addr,
83344a3780SDimitry Andric Align AddrAlign, AtomicOrdering MemOpOrder,
84e3b55780SDimitry Andric function_ref<Value *(IRBuilderBase &, Value *)> PerformOp);
85e3b55780SDimitry Andric void expandAtomicOpToLLSC(
86e3b55780SDimitry Andric Instruction *I, Type *ResultTy, Value *Addr, Align AddrAlign,
87e3b55780SDimitry Andric AtomicOrdering MemOpOrder,
88e3b55780SDimitry Andric function_ref<Value *(IRBuilderBase &, Value *)> PerformOp);
8901095a5dSDimitry Andric void expandPartwordAtomicRMW(
90145449b1SDimitry Andric AtomicRMWInst *I, TargetLoweringBase::AtomicExpansionKind ExpansionKind);
91d8e91e46SDimitry Andric AtomicRMWInst *widenPartwordAtomicRMW(AtomicRMWInst *AI);
92cfca06d7SDimitry Andric bool expandPartwordCmpXchg(AtomicCmpXchgInst *I);
93d8e91e46SDimitry Andric void expandAtomicRMWToMaskedIntrinsic(AtomicRMWInst *AI);
94d8e91e46SDimitry Andric void expandAtomicCmpXchgToMaskedIntrinsic(AtomicCmpXchgInst *CI);
9501095a5dSDimitry Andric
9601095a5dSDimitry Andric AtomicCmpXchgInst *convertCmpXchgToIntegerType(AtomicCmpXchgInst *CI);
97e3b55780SDimitry Andric static Value *insertRMWCmpXchgLoop(
98e3b55780SDimitry Andric IRBuilderBase &Builder, Type *ResultType, Value *Addr, Align AddrAlign,
99e3b55780SDimitry Andric AtomicOrdering MemOpOrder, SyncScope::ID SSID,
100e3b55780SDimitry Andric function_ref<Value *(IRBuilderBase &, Value *)> PerformOp,
10101095a5dSDimitry Andric CreateCmpXchgInstFun CreateCmpXchg);
102d8e91e46SDimitry Andric bool tryExpandAtomicCmpXchg(AtomicCmpXchgInst *CI);
10301095a5dSDimitry Andric
10467c32a98SDimitry Andric bool expandAtomicCmpXchg(AtomicCmpXchgInst *CI);
105eb11fae6SDimitry Andric bool isIdempotentRMW(AtomicRMWInst *RMWI);
106eb11fae6SDimitry Andric bool simplifyIdempotentRMW(AtomicRMWInst *RMWI);
10701095a5dSDimitry Andric
108cfca06d7SDimitry Andric bool expandAtomicOpToLibcall(Instruction *I, unsigned Size, Align Alignment,
10901095a5dSDimitry Andric Value *PointerOperand, Value *ValueOperand,
11001095a5dSDimitry Andric Value *CASExpected, AtomicOrdering Ordering,
11101095a5dSDimitry Andric AtomicOrdering Ordering2,
11201095a5dSDimitry Andric ArrayRef<RTLIB::Libcall> Libcalls);
11301095a5dSDimitry Andric void expandAtomicLoadToLibcall(LoadInst *LI);
11401095a5dSDimitry Andric void expandAtomicStoreToLibcall(StoreInst *LI);
11501095a5dSDimitry Andric void expandAtomicRMWToLibcall(AtomicRMWInst *I);
11601095a5dSDimitry Andric void expandAtomicCASToLibcall(AtomicCmpXchgInst *I);
11701095a5dSDimitry Andric
11801095a5dSDimitry Andric friend bool
11901095a5dSDimitry Andric llvm::expandAtomicRMWToCmpXchg(AtomicRMWInst *AI,
12001095a5dSDimitry Andric CreateCmpXchgInstFun CreateCmpXchg);
121ac9a064cSDimitry Andric
122ac9a064cSDimitry Andric public:
123ac9a064cSDimitry Andric bool run(Function &F, const TargetMachine *TM);
124ac9a064cSDimitry Andric };
125ac9a064cSDimitry Andric
126ac9a064cSDimitry Andric class AtomicExpandLegacy : public FunctionPass {
127ac9a064cSDimitry Andric public:
128ac9a064cSDimitry Andric static char ID; // Pass identification, replacement for typeid
129ac9a064cSDimitry Andric
AtomicExpandLegacy()130ac9a064cSDimitry Andric AtomicExpandLegacy() : FunctionPass(ID) {
131ac9a064cSDimitry Andric initializeAtomicExpandLegacyPass(*PassRegistry::getPassRegistry());
132ac9a064cSDimitry Andric }
133ac9a064cSDimitry Andric
134ac9a064cSDimitry Andric bool runOnFunction(Function &F) override;
13567c32a98SDimitry Andric };
136044eb2f6SDimitry Andric
137e3b55780SDimitry Andric // IRBuilder to be used for replacement atomic instructions.
138ac9a064cSDimitry Andric struct ReplacementIRBuilder
139ac9a064cSDimitry Andric : IRBuilder<InstSimplifyFolder, IRBuilderCallbackInserter> {
140ac9a064cSDimitry Andric MDNode *MMRAMD = nullptr;
141ac9a064cSDimitry Andric
142e3b55780SDimitry Andric // Preserves the DebugLoc from I, and preserves still valid metadata.
143ac9a064cSDimitry Andric // Enable StrictFP builder mode when appropriate.
ReplacementIRBuilder__anon6d272abd0111::ReplacementIRBuilder144e3b55780SDimitry Andric explicit ReplacementIRBuilder(Instruction *I, const DataLayout &DL)
145ac9a064cSDimitry Andric : IRBuilder(I->getContext(), DL,
146ac9a064cSDimitry Andric IRBuilderCallbackInserter(
147ac9a064cSDimitry Andric [this](Instruction *I) { addMMRAMD(I); })) {
148e3b55780SDimitry Andric SetInsertPoint(I);
149e3b55780SDimitry Andric this->CollectMetadataToCopy(I, {LLVMContext::MD_pcsections});
150ac9a064cSDimitry Andric if (BB->getParent()->getAttributes().hasFnAttr(Attribute::StrictFP))
151ac9a064cSDimitry Andric this->setIsFPConstrained(true);
152ac9a064cSDimitry Andric
153ac9a064cSDimitry Andric MMRAMD = I->getMetadata(LLVMContext::MD_mmra);
154ac9a064cSDimitry Andric }
155ac9a064cSDimitry Andric
addMMRAMD__anon6d272abd0111::ReplacementIRBuilder156ac9a064cSDimitry Andric void addMMRAMD(Instruction *I) {
157ac9a064cSDimitry Andric if (canInstructionHaveMMRAs(*I))
158ac9a064cSDimitry Andric I->setMetadata(LLVMContext::MD_mmra, MMRAMD);
159e3b55780SDimitry Andric }
160e3b55780SDimitry Andric };
161e3b55780SDimitry Andric
162044eb2f6SDimitry Andric } // end anonymous namespace
16367c32a98SDimitry Andric
164ac9a064cSDimitry Andric char AtomicExpandLegacy::ID = 0;
165044eb2f6SDimitry Andric
166ac9a064cSDimitry Andric char &llvm::AtomicExpandID = AtomicExpandLegacy::ID;
167044eb2f6SDimitry Andric
168ac9a064cSDimitry Andric INITIALIZE_PASS_BEGIN(AtomicExpandLegacy, DEBUG_TYPE,
169ac9a064cSDimitry Andric "Expand Atomic instructions", false, false)
INITIALIZE_PASS_DEPENDENCY(TargetPassConfig)170ac9a064cSDimitry Andric INITIALIZE_PASS_DEPENDENCY(TargetPassConfig)
171ac9a064cSDimitry Andric INITIALIZE_PASS_END(AtomicExpandLegacy, DEBUG_TYPE,
172ac9a064cSDimitry Andric "Expand Atomic instructions", false, false)
17367c32a98SDimitry Andric
17401095a5dSDimitry Andric // Helper functions to retrieve the size of atomic instructions.
175044eb2f6SDimitry Andric static unsigned getAtomicOpSize(LoadInst *LI) {
176ac9a064cSDimitry Andric const DataLayout &DL = LI->getDataLayout();
17701095a5dSDimitry Andric return DL.getTypeStoreSize(LI->getType());
17801095a5dSDimitry Andric }
17901095a5dSDimitry Andric
getAtomicOpSize(StoreInst * SI)180044eb2f6SDimitry Andric static unsigned getAtomicOpSize(StoreInst *SI) {
181ac9a064cSDimitry Andric const DataLayout &DL = SI->getDataLayout();
18201095a5dSDimitry Andric return DL.getTypeStoreSize(SI->getValueOperand()->getType());
18301095a5dSDimitry Andric }
18401095a5dSDimitry Andric
getAtomicOpSize(AtomicRMWInst * RMWI)185044eb2f6SDimitry Andric static unsigned getAtomicOpSize(AtomicRMWInst *RMWI) {
186ac9a064cSDimitry Andric const DataLayout &DL = RMWI->getDataLayout();
18701095a5dSDimitry Andric return DL.getTypeStoreSize(RMWI->getValOperand()->getType());
18801095a5dSDimitry Andric }
18901095a5dSDimitry Andric
getAtomicOpSize(AtomicCmpXchgInst * CASI)190044eb2f6SDimitry Andric static unsigned getAtomicOpSize(AtomicCmpXchgInst *CASI) {
191ac9a064cSDimitry Andric const DataLayout &DL = CASI->getDataLayout();
19201095a5dSDimitry Andric return DL.getTypeStoreSize(CASI->getCompareOperand()->getType());
19301095a5dSDimitry Andric }
19401095a5dSDimitry Andric
19501095a5dSDimitry Andric // Determine if a particular atomic operation has a supported size,
19601095a5dSDimitry Andric // and is of appropriate alignment, to be passed through for target
19701095a5dSDimitry Andric // lowering. (Versus turning into a __atomic libcall)
19801095a5dSDimitry Andric template <typename Inst>
atomicSizeSupported(const TargetLowering * TLI,Inst * I)199044eb2f6SDimitry Andric static bool atomicSizeSupported(const TargetLowering *TLI, Inst *I) {
20001095a5dSDimitry Andric unsigned Size = getAtomicOpSize(I);
201cfca06d7SDimitry Andric Align Alignment = I->getAlign();
202cfca06d7SDimitry Andric return Alignment >= Size &&
203cfca06d7SDimitry Andric Size <= TLI->getMaxAtomicSizeInBitsSupported() / 8;
20401095a5dSDimitry Andric }
20501095a5dSDimitry Andric
run(Function & F,const TargetMachine * TM)206ac9a064cSDimitry Andric bool AtomicExpandImpl::run(Function &F, const TargetMachine *TM) {
207ac9a064cSDimitry Andric const auto *Subtarget = TM->getSubtargetImpl(F);
208e3b55780SDimitry Andric if (!Subtarget->enableAtomicExpand())
209b5630dbaSDimitry Andric return false;
210e3b55780SDimitry Andric TLI = Subtarget->getTargetLowering();
211ac9a064cSDimitry Andric DL = &F.getDataLayout();
21267c32a98SDimitry Andric
21367c32a98SDimitry Andric SmallVector<Instruction *, 1> AtomicInsts;
21467c32a98SDimitry Andric
21567c32a98SDimitry Andric // Changing control-flow while iterating through it is a bad idea, so gather a
21667c32a98SDimitry Andric // list of all atomic instructions before we start.
217c0981da4SDimitry Andric for (Instruction &I : instructions(F))
218c0981da4SDimitry Andric if (I.isAtomic() && !isa<FenceInst>(&I))
219c0981da4SDimitry Andric AtomicInsts.push_back(&I);
22067c32a98SDimitry Andric
22167c32a98SDimitry Andric bool MadeChange = false;
2224b4fe385SDimitry Andric for (auto *I : AtomicInsts) {
22367c32a98SDimitry Andric auto LI = dyn_cast<LoadInst>(I);
22467c32a98SDimitry Andric auto SI = dyn_cast<StoreInst>(I);
22567c32a98SDimitry Andric auto RMWI = dyn_cast<AtomicRMWInst>(I);
22667c32a98SDimitry Andric auto CASI = dyn_cast<AtomicCmpXchgInst>(I);
22701095a5dSDimitry Andric assert((LI || SI || RMWI || CASI) && "Unknown atomic instruction");
22867c32a98SDimitry Andric
22901095a5dSDimitry Andric // If the Size/Alignment is not supported, replace with a libcall.
23001095a5dSDimitry Andric if (LI) {
23101095a5dSDimitry Andric if (!atomicSizeSupported(TLI, LI)) {
23201095a5dSDimitry Andric expandAtomicLoadToLibcall(LI);
23301095a5dSDimitry Andric MadeChange = true;
23401095a5dSDimitry Andric continue;
23501095a5dSDimitry Andric }
23601095a5dSDimitry Andric } else if (SI) {
23701095a5dSDimitry Andric if (!atomicSizeSupported(TLI, SI)) {
23801095a5dSDimitry Andric expandAtomicStoreToLibcall(SI);
23901095a5dSDimitry Andric MadeChange = true;
24001095a5dSDimitry Andric continue;
24101095a5dSDimitry Andric }
24201095a5dSDimitry Andric } else if (RMWI) {
24301095a5dSDimitry Andric if (!atomicSizeSupported(TLI, RMWI)) {
24401095a5dSDimitry Andric expandAtomicRMWToLibcall(RMWI);
24501095a5dSDimitry Andric MadeChange = true;
24601095a5dSDimitry Andric continue;
24701095a5dSDimitry Andric }
24801095a5dSDimitry Andric } else if (CASI) {
24901095a5dSDimitry Andric if (!atomicSizeSupported(TLI, CASI)) {
25001095a5dSDimitry Andric expandAtomicCASToLibcall(CASI);
25101095a5dSDimitry Andric MadeChange = true;
25201095a5dSDimitry Andric continue;
25301095a5dSDimitry Andric }
25401095a5dSDimitry Andric }
25501095a5dSDimitry Andric
256e3b55780SDimitry Andric if (LI && TLI->shouldCastAtomicLoadInIR(LI) ==
257e3b55780SDimitry Andric TargetLoweringBase::AtomicExpansionKind::CastToInteger) {
258e3b55780SDimitry Andric I = LI = convertAtomicLoadToIntegerType(LI);
259e3b55780SDimitry Andric MadeChange = true;
260e3b55780SDimitry Andric } else if (SI &&
261e3b55780SDimitry Andric TLI->shouldCastAtomicStoreInIR(SI) ==
262e3b55780SDimitry Andric TargetLoweringBase::AtomicExpansionKind::CastToInteger) {
263e3b55780SDimitry Andric I = SI = convertAtomicStoreToIntegerType(SI);
264e3b55780SDimitry Andric MadeChange = true;
265e3b55780SDimitry Andric } else if (RMWI &&
266e3b55780SDimitry Andric TLI->shouldCastAtomicRMWIInIR(RMWI) ==
267e3b55780SDimitry Andric TargetLoweringBase::AtomicExpansionKind::CastToInteger) {
268e3b55780SDimitry Andric I = RMWI = convertAtomicXchgToIntegerType(RMWI);
269e3b55780SDimitry Andric MadeChange = true;
270e3b55780SDimitry Andric } else if (CASI) {
271e3b55780SDimitry Andric // TODO: when we're ready to make the change at the IR level, we can
272e3b55780SDimitry Andric // extend convertCmpXchgToInteger for floating point too.
273e3b55780SDimitry Andric if (CASI->getCompareOperand()->getType()->isPointerTy()) {
274e3b55780SDimitry Andric // TODO: add a TLI hook to control this so that each target can
275e3b55780SDimitry Andric // convert to lowering the original type one at a time.
276e3b55780SDimitry Andric I = CASI = convertCmpXchgToIntegerType(CASI);
277e3b55780SDimitry Andric MadeChange = true;
278e3b55780SDimitry Andric }
279e3b55780SDimitry Andric }
280e3b55780SDimitry Andric
28101095a5dSDimitry Andric if (TLI->shouldInsertFencesForAtomic(I)) {
28201095a5dSDimitry Andric auto FenceOrdering = AtomicOrdering::Monotonic;
28301095a5dSDimitry Andric if (LI && isAcquireOrStronger(LI->getOrdering())) {
28467c32a98SDimitry Andric FenceOrdering = LI->getOrdering();
28501095a5dSDimitry Andric LI->setOrdering(AtomicOrdering::Monotonic);
28601095a5dSDimitry Andric } else if (SI && isReleaseOrStronger(SI->getOrdering())) {
28767c32a98SDimitry Andric FenceOrdering = SI->getOrdering();
28801095a5dSDimitry Andric SI->setOrdering(AtomicOrdering::Monotonic);
28901095a5dSDimitry Andric } else if (RMWI && (isReleaseOrStronger(RMWI->getOrdering()) ||
29001095a5dSDimitry Andric isAcquireOrStronger(RMWI->getOrdering()))) {
29167c32a98SDimitry Andric FenceOrdering = RMWI->getOrdering();
29201095a5dSDimitry Andric RMWI->setOrdering(AtomicOrdering::Monotonic);
293d8e91e46SDimitry Andric } else if (CASI &&
294d8e91e46SDimitry Andric TLI->shouldExpandAtomicCmpXchgInIR(CASI) ==
295d8e91e46SDimitry Andric TargetLoweringBase::AtomicExpansionKind::None &&
29601095a5dSDimitry Andric (isReleaseOrStronger(CASI->getSuccessOrdering()) ||
297344a3780SDimitry Andric isAcquireOrStronger(CASI->getSuccessOrdering()) ||
298344a3780SDimitry Andric isAcquireOrStronger(CASI->getFailureOrdering()))) {
29967c32a98SDimitry Andric // If a compare and swap is lowered to LL/SC, we can do smarter fence
30067c32a98SDimitry Andric // insertion, with a stronger one on the success path than on the
30167c32a98SDimitry Andric // failure path. As a result, fence insertion is directly done by
30267c32a98SDimitry Andric // expandAtomicCmpXchg in that case.
303344a3780SDimitry Andric FenceOrdering = CASI->getMergedOrdering();
30401095a5dSDimitry Andric CASI->setSuccessOrdering(AtomicOrdering::Monotonic);
30501095a5dSDimitry Andric CASI->setFailureOrdering(AtomicOrdering::Monotonic);
30667c32a98SDimitry Andric }
30767c32a98SDimitry Andric
30801095a5dSDimitry Andric if (FenceOrdering != AtomicOrdering::Monotonic) {
3096b3f41edSDimitry Andric MadeChange |= bracketInstWithFences(I, FenceOrdering);
31067c32a98SDimitry Andric }
311e3b55780SDimitry Andric } else if (I->hasAtomicStore() &&
312e3b55780SDimitry Andric TLI->shouldInsertTrailingFenceForAtomicStore(I)) {
313e3b55780SDimitry Andric auto FenceOrdering = AtomicOrdering::Monotonic;
314e3b55780SDimitry Andric if (SI)
315e3b55780SDimitry Andric FenceOrdering = SI->getOrdering();
316e3b55780SDimitry Andric else if (RMWI)
317e3b55780SDimitry Andric FenceOrdering = RMWI->getOrdering();
318e3b55780SDimitry Andric else if (CASI && TLI->shouldExpandAtomicCmpXchgInIR(CASI) !=
319e3b55780SDimitry Andric TargetLoweringBase::AtomicExpansionKind::LLSC)
320e3b55780SDimitry Andric // LLSC is handled in expandAtomicCmpXchg().
321e3b55780SDimitry Andric FenceOrdering = CASI->getSuccessOrdering();
32267c32a98SDimitry Andric
323e3b55780SDimitry Andric IRBuilder Builder(I);
324e3b55780SDimitry Andric if (auto TrailingFence =
325e3b55780SDimitry Andric TLI->emitTrailingFence(Builder, I, FenceOrdering)) {
326e3b55780SDimitry Andric TrailingFence->moveAfter(I);
327dd58ef01SDimitry Andric MadeChange = true;
328dd58ef01SDimitry Andric }
329e3b55780SDimitry Andric }
330dd58ef01SDimitry Andric
331e3b55780SDimitry Andric if (LI)
332dd58ef01SDimitry Andric MadeChange |= tryExpandAtomicLoad(LI);
333e3b55780SDimitry Andric else if (SI)
334e3b55780SDimitry Andric MadeChange |= tryExpandAtomicStore(SI);
335e3b55780SDimitry Andric else if (RMWI) {
33667c32a98SDimitry Andric // There are two different ways of expanding RMW instructions:
33767c32a98SDimitry Andric // - into a load if it is idempotent
33867c32a98SDimitry Andric // - into a Cmpxchg/LL-SC loop otherwise
33967c32a98SDimitry Andric // we try them in that order.
3405a5ac124SDimitry Andric
3415a5ac124SDimitry Andric if (isIdempotentRMW(RMWI) && simplifyIdempotentRMW(RMWI)) {
3425a5ac124SDimitry Andric MadeChange = true;
3435a5ac124SDimitry Andric } else {
3445a5ac124SDimitry Andric MadeChange |= tryExpandAtomicRMW(RMWI);
3455a5ac124SDimitry Andric }
346e3b55780SDimitry Andric } else if (CASI)
347d8e91e46SDimitry Andric MadeChange |= tryExpandAtomicCmpXchg(CASI);
34867c32a98SDimitry Andric }
34967c32a98SDimitry Andric return MadeChange;
35067c32a98SDimitry Andric }
35167c32a98SDimitry Andric
runOnFunction(Function & F)352ac9a064cSDimitry Andric bool AtomicExpandLegacy::runOnFunction(Function &F) {
353ac9a064cSDimitry Andric
354ac9a064cSDimitry Andric auto *TPC = getAnalysisIfAvailable<TargetPassConfig>();
355ac9a064cSDimitry Andric if (!TPC)
356ac9a064cSDimitry Andric return false;
357ac9a064cSDimitry Andric auto *TM = &TPC->getTM<TargetMachine>();
358ac9a064cSDimitry Andric AtomicExpandImpl AE;
359ac9a064cSDimitry Andric return AE.run(F, TM);
360ac9a064cSDimitry Andric }
361ac9a064cSDimitry Andric
createAtomicExpandLegacyPass()362ac9a064cSDimitry Andric FunctionPass *llvm::createAtomicExpandLegacyPass() {
363ac9a064cSDimitry Andric return new AtomicExpandLegacy();
364ac9a064cSDimitry Andric }
365ac9a064cSDimitry Andric
run(Function & F,FunctionAnalysisManager & AM)366ac9a064cSDimitry Andric PreservedAnalyses AtomicExpandPass::run(Function &F,
367ac9a064cSDimitry Andric FunctionAnalysisManager &AM) {
368ac9a064cSDimitry Andric AtomicExpandImpl AE;
369ac9a064cSDimitry Andric
370ac9a064cSDimitry Andric bool Changed = AE.run(F, TM);
371ac9a064cSDimitry Andric if (!Changed)
372ac9a064cSDimitry Andric return PreservedAnalyses::all();
373ac9a064cSDimitry Andric
374ac9a064cSDimitry Andric return PreservedAnalyses::none();
375ac9a064cSDimitry Andric }
376ac9a064cSDimitry Andric
bracketInstWithFences(Instruction * I,AtomicOrdering Order)377ac9a064cSDimitry Andric bool AtomicExpandImpl::bracketInstWithFences(Instruction *I,
378ac9a064cSDimitry Andric AtomicOrdering Order) {
379e3b55780SDimitry Andric ReplacementIRBuilder Builder(I, *DL);
38067c32a98SDimitry Andric
3816b3f41edSDimitry Andric auto LeadingFence = TLI->emitLeadingFence(Builder, I, Order);
38267c32a98SDimitry Andric
3836b3f41edSDimitry Andric auto TrailingFence = TLI->emitTrailingFence(Builder, I, Order);
38467c32a98SDimitry Andric // We have a guard here because not every atomic operation generates a
38567c32a98SDimitry Andric // trailing fence.
386044eb2f6SDimitry Andric if (TrailingFence)
387044eb2f6SDimitry Andric TrailingFence->moveAfter(I);
38867c32a98SDimitry Andric
38967c32a98SDimitry Andric return (LeadingFence || TrailingFence);
39067c32a98SDimitry Andric }
39167c32a98SDimitry Andric
392dd58ef01SDimitry Andric /// Get the iX type with the same bitwidth as T.
393ac9a064cSDimitry Andric IntegerType *
getCorrespondingIntegerType(Type * T,const DataLayout & DL)394ac9a064cSDimitry Andric AtomicExpandImpl::getCorrespondingIntegerType(Type *T, const DataLayout &DL) {
395e6d15924SDimitry Andric EVT VT = TLI->getMemValueType(DL, T);
396dd58ef01SDimitry Andric unsigned BitWidth = VT.getStoreSizeInBits();
397dd58ef01SDimitry Andric assert(BitWidth == VT.getSizeInBits() && "must be a power of two");
398dd58ef01SDimitry Andric return IntegerType::get(T->getContext(), BitWidth);
399dd58ef01SDimitry Andric }
400dd58ef01SDimitry Andric
401dd58ef01SDimitry Andric /// Convert an atomic load of a non-integral type to an integer load of the
40201095a5dSDimitry Andric /// equivalent bitwidth. See the function comment on
403dd58ef01SDimitry Andric /// convertAtomicStoreToIntegerType for background.
convertAtomicLoadToIntegerType(LoadInst * LI)404ac9a064cSDimitry Andric LoadInst *AtomicExpandImpl::convertAtomicLoadToIntegerType(LoadInst *LI) {
405dd58ef01SDimitry Andric auto *M = LI->getModule();
406145449b1SDimitry Andric Type *NewTy = getCorrespondingIntegerType(LI->getType(), M->getDataLayout());
407dd58ef01SDimitry Andric
408e3b55780SDimitry Andric ReplacementIRBuilder Builder(LI, *DL);
409dd58ef01SDimitry Andric
410dd58ef01SDimitry Andric Value *Addr = LI->getPointerOperand();
411dd58ef01SDimitry Andric
412b1c73532SDimitry Andric auto *NewLI = Builder.CreateLoad(NewTy, Addr);
413cfca06d7SDimitry Andric NewLI->setAlignment(LI->getAlign());
414dd58ef01SDimitry Andric NewLI->setVolatile(LI->isVolatile());
415ca089b24SDimitry Andric NewLI->setAtomic(LI->getOrdering(), LI->getSyncScopeID());
416eb11fae6SDimitry Andric LLVM_DEBUG(dbgs() << "Replaced " << *LI << " with " << *NewLI << "\n");
417dd58ef01SDimitry Andric
418dd58ef01SDimitry Andric Value *NewVal = Builder.CreateBitCast(NewLI, LI->getType());
419dd58ef01SDimitry Andric LI->replaceAllUsesWith(NewVal);
420dd58ef01SDimitry Andric LI->eraseFromParent();
421dd58ef01SDimitry Andric return NewLI;
422dd58ef01SDimitry Andric }
423dd58ef01SDimitry Andric
424344a3780SDimitry Andric AtomicRMWInst *
convertAtomicXchgToIntegerType(AtomicRMWInst * RMWI)425ac9a064cSDimitry Andric AtomicExpandImpl::convertAtomicXchgToIntegerType(AtomicRMWInst *RMWI) {
426344a3780SDimitry Andric auto *M = RMWI->getModule();
427344a3780SDimitry Andric Type *NewTy =
428344a3780SDimitry Andric getCorrespondingIntegerType(RMWI->getType(), M->getDataLayout());
429344a3780SDimitry Andric
430e3b55780SDimitry Andric ReplacementIRBuilder Builder(RMWI, *DL);
431344a3780SDimitry Andric
432344a3780SDimitry Andric Value *Addr = RMWI->getPointerOperand();
433344a3780SDimitry Andric Value *Val = RMWI->getValOperand();
434145449b1SDimitry Andric Value *NewVal = Val->getType()->isPointerTy()
435145449b1SDimitry Andric ? Builder.CreatePtrToInt(Val, NewTy)
436145449b1SDimitry Andric : Builder.CreateBitCast(Val, NewTy);
437344a3780SDimitry Andric
438ac9a064cSDimitry Andric auto *NewRMWI = Builder.CreateAtomicRMW(AtomicRMWInst::Xchg, Addr, NewVal,
439ac9a064cSDimitry Andric RMWI->getAlign(), RMWI->getOrdering(),
440ac9a064cSDimitry Andric RMWI->getSyncScopeID());
441344a3780SDimitry Andric NewRMWI->setVolatile(RMWI->isVolatile());
442344a3780SDimitry Andric LLVM_DEBUG(dbgs() << "Replaced " << *RMWI << " with " << *NewRMWI << "\n");
443344a3780SDimitry Andric
444145449b1SDimitry Andric Value *NewRVal = RMWI->getType()->isPointerTy()
445145449b1SDimitry Andric ? Builder.CreateIntToPtr(NewRMWI, RMWI->getType())
446145449b1SDimitry Andric : Builder.CreateBitCast(NewRMWI, RMWI->getType());
447344a3780SDimitry Andric RMWI->replaceAllUsesWith(NewRVal);
448344a3780SDimitry Andric RMWI->eraseFromParent();
449344a3780SDimitry Andric return NewRMWI;
450344a3780SDimitry Andric }
451344a3780SDimitry Andric
tryExpandAtomicLoad(LoadInst * LI)452ac9a064cSDimitry Andric bool AtomicExpandImpl::tryExpandAtomicLoad(LoadInst *LI) {
453dd58ef01SDimitry Andric switch (TLI->shouldExpandAtomicLoadInIR(LI)) {
454dd58ef01SDimitry Andric case TargetLoweringBase::AtomicExpansionKind::None:
455dd58ef01SDimitry Andric return false;
456dd58ef01SDimitry Andric case TargetLoweringBase::AtomicExpansionKind::LLSC:
45701095a5dSDimitry Andric expandAtomicOpToLLSC(
458344a3780SDimitry Andric LI, LI->getType(), LI->getPointerOperand(), LI->getAlign(),
459344a3780SDimitry Andric LI->getOrdering(),
460e3b55780SDimitry Andric [](IRBuilderBase &Builder, Value *Loaded) { return Loaded; });
46101095a5dSDimitry Andric return true;
462dd58ef01SDimitry Andric case TargetLoweringBase::AtomicExpansionKind::LLOnly:
46367c32a98SDimitry Andric return expandAtomicLoadToLL(LI);
464dd58ef01SDimitry Andric case TargetLoweringBase::AtomicExpansionKind::CmpXChg:
46567c32a98SDimitry Andric return expandAtomicLoadToCmpXchg(LI);
466145449b1SDimitry Andric case TargetLoweringBase::AtomicExpansionKind::NotAtomic:
467145449b1SDimitry Andric LI->setAtomic(AtomicOrdering::NotAtomic);
468145449b1SDimitry Andric return true;
469d8e91e46SDimitry Andric default:
470dd58ef01SDimitry Andric llvm_unreachable("Unhandled case in tryExpandAtomicLoad");
471dd58ef01SDimitry Andric }
472d8e91e46SDimitry Andric }
47367c32a98SDimitry Andric
tryExpandAtomicStore(StoreInst * SI)474ac9a064cSDimitry Andric bool AtomicExpandImpl::tryExpandAtomicStore(StoreInst *SI) {
475145449b1SDimitry Andric switch (TLI->shouldExpandAtomicStoreInIR(SI)) {
476145449b1SDimitry Andric case TargetLoweringBase::AtomicExpansionKind::None:
477145449b1SDimitry Andric return false;
478145449b1SDimitry Andric case TargetLoweringBase::AtomicExpansionKind::Expand:
479145449b1SDimitry Andric expandAtomicStore(SI);
480145449b1SDimitry Andric return true;
481145449b1SDimitry Andric case TargetLoweringBase::AtomicExpansionKind::NotAtomic:
482145449b1SDimitry Andric SI->setAtomic(AtomicOrdering::NotAtomic);
483145449b1SDimitry Andric return true;
484145449b1SDimitry Andric default:
485145449b1SDimitry Andric llvm_unreachable("Unhandled case in tryExpandAtomicStore");
486145449b1SDimitry Andric }
487145449b1SDimitry Andric }
488145449b1SDimitry Andric
expandAtomicLoadToLL(LoadInst * LI)489ac9a064cSDimitry Andric bool AtomicExpandImpl::expandAtomicLoadToLL(LoadInst *LI) {
490e3b55780SDimitry Andric ReplacementIRBuilder Builder(LI, *DL);
49167c32a98SDimitry Andric
49267c32a98SDimitry Andric // On some architectures, load-linked instructions are atomic for larger
49367c32a98SDimitry Andric // sizes than normal loads. For example, the only 64-bit load guaranteed
49467c32a98SDimitry Andric // to be single-copy atomic by ARM is an ldrexd (A3.5.3).
495344a3780SDimitry Andric Value *Val = TLI->emitLoadLinked(Builder, LI->getType(),
496344a3780SDimitry Andric LI->getPointerOperand(), LI->getOrdering());
497dd58ef01SDimitry Andric TLI->emitAtomicCmpXchgNoStoreLLBalance(Builder);
49867c32a98SDimitry Andric
49967c32a98SDimitry Andric LI->replaceAllUsesWith(Val);
50067c32a98SDimitry Andric LI->eraseFromParent();
50167c32a98SDimitry Andric
50267c32a98SDimitry Andric return true;
50367c32a98SDimitry Andric }
50467c32a98SDimitry Andric
expandAtomicLoadToCmpXchg(LoadInst * LI)505ac9a064cSDimitry Andric bool AtomicExpandImpl::expandAtomicLoadToCmpXchg(LoadInst *LI) {
506e3b55780SDimitry Andric ReplacementIRBuilder Builder(LI, *DL);
50767c32a98SDimitry Andric AtomicOrdering Order = LI->getOrdering();
508e6d15924SDimitry Andric if (Order == AtomicOrdering::Unordered)
509e6d15924SDimitry Andric Order = AtomicOrdering::Monotonic;
510e6d15924SDimitry Andric
51167c32a98SDimitry Andric Value *Addr = LI->getPointerOperand();
512344a3780SDimitry Andric Type *Ty = LI->getType();
51367c32a98SDimitry Andric Constant *DummyVal = Constant::getNullValue(Ty);
51467c32a98SDimitry Andric
51567c32a98SDimitry Andric Value *Pair = Builder.CreateAtomicCmpXchg(
516344a3780SDimitry Andric Addr, DummyVal, DummyVal, LI->getAlign(), Order,
51767c32a98SDimitry Andric AtomicCmpXchgInst::getStrongestFailureOrdering(Order));
51867c32a98SDimitry Andric Value *Loaded = Builder.CreateExtractValue(Pair, 0, "loaded");
51967c32a98SDimitry Andric
52067c32a98SDimitry Andric LI->replaceAllUsesWith(Loaded);
52167c32a98SDimitry Andric LI->eraseFromParent();
52267c32a98SDimitry Andric
52367c32a98SDimitry Andric return true;
52467c32a98SDimitry Andric }
52567c32a98SDimitry Andric
526dd58ef01SDimitry Andric /// Convert an atomic store of a non-integral type to an integer store of the
52701095a5dSDimitry Andric /// equivalent bitwidth. We used to not support floating point or vector
528dd58ef01SDimitry Andric /// atomics in the IR at all. The backends learned to deal with the bitcast
529dd58ef01SDimitry Andric /// idiom because that was the only way of expressing the notion of a atomic
530dd58ef01SDimitry Andric /// float or vector store. The long term plan is to teach each backend to
531dd58ef01SDimitry Andric /// instruction select from the original atomic store, but as a migration
532dd58ef01SDimitry Andric /// mechanism, we convert back to the old format which the backends understand.
533dd58ef01SDimitry Andric /// Each backend will need individual work to recognize the new format.
convertAtomicStoreToIntegerType(StoreInst * SI)534ac9a064cSDimitry Andric StoreInst *AtomicExpandImpl::convertAtomicStoreToIntegerType(StoreInst *SI) {
535e3b55780SDimitry Andric ReplacementIRBuilder Builder(SI, *DL);
536dd58ef01SDimitry Andric auto *M = SI->getModule();
537dd58ef01SDimitry Andric Type *NewTy = getCorrespondingIntegerType(SI->getValueOperand()->getType(),
538dd58ef01SDimitry Andric M->getDataLayout());
539dd58ef01SDimitry Andric Value *NewVal = Builder.CreateBitCast(SI->getValueOperand(), NewTy);
540dd58ef01SDimitry Andric
541dd58ef01SDimitry Andric Value *Addr = SI->getPointerOperand();
542dd58ef01SDimitry Andric
543b1c73532SDimitry Andric StoreInst *NewSI = Builder.CreateStore(NewVal, Addr);
544cfca06d7SDimitry Andric NewSI->setAlignment(SI->getAlign());
545dd58ef01SDimitry Andric NewSI->setVolatile(SI->isVolatile());
546ca089b24SDimitry Andric NewSI->setAtomic(SI->getOrdering(), SI->getSyncScopeID());
547eb11fae6SDimitry Andric LLVM_DEBUG(dbgs() << "Replaced " << *SI << " with " << *NewSI << "\n");
548dd58ef01SDimitry Andric SI->eraseFromParent();
549dd58ef01SDimitry Andric return NewSI;
550dd58ef01SDimitry Andric }
551dd58ef01SDimitry Andric
expandAtomicStore(StoreInst * SI)552ac9a064cSDimitry Andric void AtomicExpandImpl::expandAtomicStore(StoreInst *SI) {
55367c32a98SDimitry Andric // This function is only called on atomic stores that are too large to be
55467c32a98SDimitry Andric // atomic if implemented as a native store. So we replace them by an
55567c32a98SDimitry Andric // atomic swap, that can be implemented for example as a ldrex/strex on ARM
55667c32a98SDimitry Andric // or lock cmpxchg8/16b on X86, as these are atomic for larger sizes.
5575a5ac124SDimitry Andric // It is the responsibility of the target to only signal expansion via
55867c32a98SDimitry Andric // shouldExpandAtomicRMW in cases where this is required and possible.
559e3b55780SDimitry Andric ReplacementIRBuilder Builder(SI, *DL);
56008e8dd7bSDimitry Andric AtomicOrdering Ordering = SI->getOrdering();
56108e8dd7bSDimitry Andric assert(Ordering != AtomicOrdering::NotAtomic);
56208e8dd7bSDimitry Andric AtomicOrdering RMWOrdering = Ordering == AtomicOrdering::Unordered
56308e8dd7bSDimitry Andric ? AtomicOrdering::Monotonic
56408e8dd7bSDimitry Andric : Ordering;
565344a3780SDimitry Andric AtomicRMWInst *AI = Builder.CreateAtomicRMW(
566344a3780SDimitry Andric AtomicRMWInst::Xchg, SI->getPointerOperand(), SI->getValueOperand(),
56708e8dd7bSDimitry Andric SI->getAlign(), RMWOrdering);
56867c32a98SDimitry Andric SI->eraseFromParent();
56967c32a98SDimitry Andric
57067c32a98SDimitry Andric // Now we have an appropriate swap instruction, lower it as usual.
571145449b1SDimitry Andric tryExpandAtomicRMW(AI);
57267c32a98SDimitry Andric }
57367c32a98SDimitry Andric
createCmpXchgInstFun(IRBuilderBase & Builder,Value * Addr,Value * Loaded,Value * NewVal,Align AddrAlign,AtomicOrdering MemOpOrder,SyncScope::ID SSID,Value * & Success,Value * & NewLoaded)574e3b55780SDimitry Andric static void createCmpXchgInstFun(IRBuilderBase &Builder, Value *Addr,
575344a3780SDimitry Andric Value *Loaded, Value *NewVal, Align AddrAlign,
576344a3780SDimitry Andric AtomicOrdering MemOpOrder, SyncScope::ID SSID,
577dd58ef01SDimitry Andric Value *&Success, Value *&NewLoaded) {
578e6d15924SDimitry Andric Type *OrigTy = NewVal->getType();
579e6d15924SDimitry Andric
580ac9a064cSDimitry Andric // This code can go away when cmpxchg supports FP and vector types.
581145449b1SDimitry Andric assert(!OrigTy->isPointerTy());
582ac9a064cSDimitry Andric bool NeedBitcast = OrigTy->isFloatingPointTy() || OrigTy->isVectorTy();
583e6d15924SDimitry Andric if (NeedBitcast) {
584e6d15924SDimitry Andric IntegerType *IntTy = Builder.getIntNTy(OrigTy->getPrimitiveSizeInBits());
585e6d15924SDimitry Andric NewVal = Builder.CreateBitCast(NewVal, IntTy);
586e6d15924SDimitry Andric Loaded = Builder.CreateBitCast(Loaded, IntTy);
587e6d15924SDimitry Andric }
588e6d15924SDimitry Andric
589dd58ef01SDimitry Andric Value *Pair = Builder.CreateAtomicCmpXchg(
590344a3780SDimitry Andric Addr, Loaded, NewVal, AddrAlign, MemOpOrder,
591344a3780SDimitry Andric AtomicCmpXchgInst::getStrongestFailureOrdering(MemOpOrder), SSID);
592dd58ef01SDimitry Andric Success = Builder.CreateExtractValue(Pair, 1, "success");
593dd58ef01SDimitry Andric NewLoaded = Builder.CreateExtractValue(Pair, 0, "newloaded");
594e6d15924SDimitry Andric
595e6d15924SDimitry Andric if (NeedBitcast)
596e6d15924SDimitry Andric NewLoaded = Builder.CreateBitCast(NewLoaded, OrigTy);
5975a5ac124SDimitry Andric }
59867c32a98SDimitry Andric
tryExpandAtomicRMW(AtomicRMWInst * AI)599ac9a064cSDimitry Andric bool AtomicExpandImpl::tryExpandAtomicRMW(AtomicRMWInst *AI) {
600c0981da4SDimitry Andric LLVMContext &Ctx = AI->getModule()->getContext();
601c0981da4SDimitry Andric TargetLowering::AtomicExpansionKind Kind = TLI->shouldExpandAtomicRMWInIR(AI);
602c0981da4SDimitry Andric switch (Kind) {
603dd58ef01SDimitry Andric case TargetLoweringBase::AtomicExpansionKind::None:
604dd58ef01SDimitry Andric return false;
60501095a5dSDimitry Andric case TargetLoweringBase::AtomicExpansionKind::LLSC: {
60601095a5dSDimitry Andric unsigned MinCASSize = TLI->getMinCmpXchgSizeInBits() / 8;
60701095a5dSDimitry Andric unsigned ValueSize = getAtomicOpSize(AI);
60801095a5dSDimitry Andric if (ValueSize < MinCASSize) {
609cfca06d7SDimitry Andric expandPartwordAtomicRMW(AI,
610cfca06d7SDimitry Andric TargetLoweringBase::AtomicExpansionKind::LLSC);
61101095a5dSDimitry Andric } else {
612e3b55780SDimitry Andric auto PerformOp = [&](IRBuilderBase &Builder, Value *Loaded) {
613145449b1SDimitry Andric return buildAtomicRMWValue(AI->getOperation(), Builder, Loaded,
614dd58ef01SDimitry Andric AI->getValOperand());
61501095a5dSDimitry Andric };
61601095a5dSDimitry Andric expandAtomicOpToLLSC(AI, AI->getType(), AI->getPointerOperand(),
617344a3780SDimitry Andric AI->getAlign(), AI->getOrdering(), PerformOp);
61801095a5dSDimitry Andric }
61901095a5dSDimitry Andric return true;
62001095a5dSDimitry Andric }
62101095a5dSDimitry Andric case TargetLoweringBase::AtomicExpansionKind::CmpXChg: {
62201095a5dSDimitry Andric unsigned MinCASSize = TLI->getMinCmpXchgSizeInBits() / 8;
62301095a5dSDimitry Andric unsigned ValueSize = getAtomicOpSize(AI);
62401095a5dSDimitry Andric if (ValueSize < MinCASSize) {
62501095a5dSDimitry Andric expandPartwordAtomicRMW(AI,
62601095a5dSDimitry Andric TargetLoweringBase::AtomicExpansionKind::CmpXChg);
62701095a5dSDimitry Andric } else {
628c0981da4SDimitry Andric SmallVector<StringRef> SSNs;
629c0981da4SDimitry Andric Ctx.getSyncScopeNames(SSNs);
630c0981da4SDimitry Andric auto MemScope = SSNs[AI->getSyncScopeID()].empty()
631c0981da4SDimitry Andric ? "system"
632c0981da4SDimitry Andric : SSNs[AI->getSyncScopeID()];
633c0981da4SDimitry Andric OptimizationRemarkEmitter ORE(AI->getFunction());
634c0981da4SDimitry Andric ORE.emit([&]() {
635c0981da4SDimitry Andric return OptimizationRemark(DEBUG_TYPE, "Passed", AI)
636c0981da4SDimitry Andric << "A compare and swap loop was generated for an atomic "
637c0981da4SDimitry Andric << AI->getOperationName(AI->getOperation()) << " operation at "
638c0981da4SDimitry Andric << MemScope << " memory scope";
639c0981da4SDimitry Andric });
64001095a5dSDimitry Andric expandAtomicRMWToCmpXchg(AI, createCmpXchgInstFun);
64101095a5dSDimitry Andric }
64201095a5dSDimitry Andric return true;
64301095a5dSDimitry Andric }
644d8e91e46SDimitry Andric case TargetLoweringBase::AtomicExpansionKind::MaskedIntrinsic: {
645ac9a064cSDimitry Andric unsigned MinCASSize = TLI->getMinCmpXchgSizeInBits() / 8;
646ac9a064cSDimitry Andric unsigned ValueSize = getAtomicOpSize(AI);
647ac9a064cSDimitry Andric if (ValueSize < MinCASSize) {
648ac9a064cSDimitry Andric AtomicRMWInst::BinOp Op = AI->getOperation();
649ac9a064cSDimitry Andric // Widen And/Or/Xor and give the target another chance at expanding it.
650ac9a064cSDimitry Andric if (Op == AtomicRMWInst::Or || Op == AtomicRMWInst::Xor ||
651ac9a064cSDimitry Andric Op == AtomicRMWInst::And) {
652ac9a064cSDimitry Andric tryExpandAtomicRMW(widenPartwordAtomicRMW(AI));
653ac9a064cSDimitry Andric return true;
654ac9a064cSDimitry Andric }
655ac9a064cSDimitry Andric }
656d8e91e46SDimitry Andric expandAtomicRMWToMaskedIntrinsic(AI);
657d8e91e46SDimitry Andric return true;
658d8e91e46SDimitry Andric }
659145449b1SDimitry Andric case TargetLoweringBase::AtomicExpansionKind::BitTestIntrinsic: {
660145449b1SDimitry Andric TLI->emitBitTestAtomicRMWIntrinsic(AI);
661145449b1SDimitry Andric return true;
662145449b1SDimitry Andric }
663e3b55780SDimitry Andric case TargetLoweringBase::AtomicExpansionKind::CmpArithIntrinsic: {
664e3b55780SDimitry Andric TLI->emitCmpArithAtomicRMWIntrinsic(AI);
665e3b55780SDimitry Andric return true;
666e3b55780SDimitry Andric }
667145449b1SDimitry Andric case TargetLoweringBase::AtomicExpansionKind::NotAtomic:
668145449b1SDimitry Andric return lowerAtomicRMWInst(AI);
669e3b55780SDimitry Andric case TargetLoweringBase::AtomicExpansionKind::Expand:
670e3b55780SDimitry Andric TLI->emitExpandAtomicRMW(AI);
671e3b55780SDimitry Andric return true;
672dd58ef01SDimitry Andric default:
673dd58ef01SDimitry Andric llvm_unreachable("Unhandled case in tryExpandAtomicRMW");
674dd58ef01SDimitry Andric }
675dd58ef01SDimitry Andric }
676dd58ef01SDimitry Andric
67701095a5dSDimitry Andric namespace {
67801095a5dSDimitry Andric
67901095a5dSDimitry Andric struct PartwordMaskValues {
680cfca06d7SDimitry Andric // These three fields are guaranteed to be set by createMaskInstrs.
681cfca06d7SDimitry Andric Type *WordType = nullptr;
682cfca06d7SDimitry Andric Type *ValueType = nullptr;
683e3b55780SDimitry Andric Type *IntValueType = nullptr;
684cfca06d7SDimitry Andric Value *AlignedAddr = nullptr;
685344a3780SDimitry Andric Align AlignedAddrAlignment;
686cfca06d7SDimitry Andric // The remaining fields can be null.
687cfca06d7SDimitry Andric Value *ShiftAmt = nullptr;
688cfca06d7SDimitry Andric Value *Mask = nullptr;
689cfca06d7SDimitry Andric Value *Inv_Mask = nullptr;
69001095a5dSDimitry Andric };
691044eb2f6SDimitry Andric
692cfca06d7SDimitry Andric LLVM_ATTRIBUTE_UNUSED
operator <<(raw_ostream & O,const PartwordMaskValues & PMV)693cfca06d7SDimitry Andric raw_ostream &operator<<(raw_ostream &O, const PartwordMaskValues &PMV) {
694cfca06d7SDimitry Andric auto PrintObj = [&O](auto *V) {
695cfca06d7SDimitry Andric if (V)
696cfca06d7SDimitry Andric O << *V;
697cfca06d7SDimitry Andric else
698cfca06d7SDimitry Andric O << "nullptr";
699cfca06d7SDimitry Andric O << '\n';
700cfca06d7SDimitry Andric };
701cfca06d7SDimitry Andric O << "PartwordMaskValues {\n";
702cfca06d7SDimitry Andric O << " WordType: ";
703cfca06d7SDimitry Andric PrintObj(PMV.WordType);
704cfca06d7SDimitry Andric O << " ValueType: ";
705cfca06d7SDimitry Andric PrintObj(PMV.ValueType);
706cfca06d7SDimitry Andric O << " AlignedAddr: ";
707cfca06d7SDimitry Andric PrintObj(PMV.AlignedAddr);
708344a3780SDimitry Andric O << " AlignedAddrAlignment: " << PMV.AlignedAddrAlignment.value() << '\n';
709cfca06d7SDimitry Andric O << " ShiftAmt: ";
710cfca06d7SDimitry Andric PrintObj(PMV.ShiftAmt);
711cfca06d7SDimitry Andric O << " Mask: ";
712cfca06d7SDimitry Andric PrintObj(PMV.Mask);
713cfca06d7SDimitry Andric O << " Inv_Mask: ";
714cfca06d7SDimitry Andric PrintObj(PMV.Inv_Mask);
715cfca06d7SDimitry Andric O << "}\n";
716cfca06d7SDimitry Andric return O;
717cfca06d7SDimitry Andric }
718cfca06d7SDimitry Andric
71901095a5dSDimitry Andric } // end anonymous namespace
72001095a5dSDimitry Andric
72101095a5dSDimitry Andric /// This is a helper function which builds instructions to provide
72201095a5dSDimitry Andric /// values necessary for partword atomic operations. It takes an
72301095a5dSDimitry Andric /// incoming address, Addr, and ValueType, and constructs the address,
72401095a5dSDimitry Andric /// shift-amounts and masks needed to work with a larger value of size
72501095a5dSDimitry Andric /// WordSize.
72601095a5dSDimitry Andric ///
72701095a5dSDimitry Andric /// AlignedAddr: Addr rounded down to a multiple of WordSize
72801095a5dSDimitry Andric ///
72901095a5dSDimitry Andric /// ShiftAmt: Number of bits to right-shift a WordSize value loaded
73001095a5dSDimitry Andric /// from AlignAddr for it to have the same value as if
73101095a5dSDimitry Andric /// ValueType was loaded from Addr.
73201095a5dSDimitry Andric ///
73301095a5dSDimitry Andric /// Mask: Value to mask with the value loaded from AlignAddr to
73401095a5dSDimitry Andric /// include only the part that would've been loaded from Addr.
73501095a5dSDimitry Andric ///
73601095a5dSDimitry Andric /// Inv_Mask: The inverse of Mask.
createMaskInstrs(IRBuilderBase & Builder,Instruction * I,Type * ValueType,Value * Addr,Align AddrAlign,unsigned MinWordSize)737e3b55780SDimitry Andric static PartwordMaskValues createMaskInstrs(IRBuilderBase &Builder,
738e3b55780SDimitry Andric Instruction *I, Type *ValueType,
739e3b55780SDimitry Andric Value *Addr, Align AddrAlign,
740cfca06d7SDimitry Andric unsigned MinWordSize) {
741cfca06d7SDimitry Andric PartwordMaskValues PMV;
74201095a5dSDimitry Andric
74301095a5dSDimitry Andric Module *M = I->getModule();
744cfca06d7SDimitry Andric LLVMContext &Ctx = M->getContext();
74501095a5dSDimitry Andric const DataLayout &DL = M->getDataLayout();
74601095a5dSDimitry Andric unsigned ValueSize = DL.getTypeStoreSize(ValueType);
74701095a5dSDimitry Andric
748e3b55780SDimitry Andric PMV.ValueType = PMV.IntValueType = ValueType;
749ac9a064cSDimitry Andric if (PMV.ValueType->isFloatingPointTy() || PMV.ValueType->isVectorTy())
750e3b55780SDimitry Andric PMV.IntValueType =
751e3b55780SDimitry Andric Type::getIntNTy(Ctx, ValueType->getPrimitiveSizeInBits());
752e3b55780SDimitry Andric
753cfca06d7SDimitry Andric PMV.WordType = MinWordSize > ValueSize ? Type::getIntNTy(Ctx, MinWordSize * 8)
754cfca06d7SDimitry Andric : ValueType;
755cfca06d7SDimitry Andric if (PMV.ValueType == PMV.WordType) {
756cfca06d7SDimitry Andric PMV.AlignedAddr = Addr;
757344a3780SDimitry Andric PMV.AlignedAddrAlignment = AddrAlign;
758344a3780SDimitry Andric PMV.ShiftAmt = ConstantInt::get(PMV.ValueType, 0);
759145449b1SDimitry Andric PMV.Mask = ConstantInt::get(PMV.ValueType, ~0, /*isSigned*/ true);
760cfca06d7SDimitry Andric return PMV;
76101095a5dSDimitry Andric }
76201095a5dSDimitry Andric
763344a3780SDimitry Andric PMV.AlignedAddrAlignment = Align(MinWordSize);
764cfca06d7SDimitry Andric
765e3b55780SDimitry Andric assert(ValueSize < MinWordSize);
766e3b55780SDimitry Andric
767e3b55780SDimitry Andric PointerType *PtrTy = cast<PointerType>(Addr->getType());
768ac9a064cSDimitry Andric IntegerType *IntTy = DL.getIndexType(Ctx, PtrTy->getAddressSpace());
769e3b55780SDimitry Andric Value *PtrLSB;
770e3b55780SDimitry Andric
771e3b55780SDimitry Andric if (AddrAlign < MinWordSize) {
772e3b55780SDimitry Andric PMV.AlignedAddr = Builder.CreateIntrinsic(
773e3b55780SDimitry Andric Intrinsic::ptrmask, {PtrTy, IntTy},
774e3b55780SDimitry Andric {Addr, ConstantInt::get(IntTy, ~(uint64_t)(MinWordSize - 1))}, nullptr,
775e3b55780SDimitry Andric "AlignedAddr");
776e3b55780SDimitry Andric
777e3b55780SDimitry Andric Value *AddrInt = Builder.CreatePtrToInt(Addr, IntTy);
778e3b55780SDimitry Andric PtrLSB = Builder.CreateAnd(AddrInt, MinWordSize - 1, "PtrLSB");
779e3b55780SDimitry Andric } else {
780e3b55780SDimitry Andric // If the alignment is high enough, the LSB are known 0.
781e3b55780SDimitry Andric PMV.AlignedAddr = Addr;
782e3b55780SDimitry Andric PtrLSB = ConstantInt::getNullValue(IntTy);
783e3b55780SDimitry Andric }
784e3b55780SDimitry Andric
785cfca06d7SDimitry Andric if (DL.isLittleEndian()) {
786cfca06d7SDimitry Andric // turn bytes into bits
787cfca06d7SDimitry Andric PMV.ShiftAmt = Builder.CreateShl(PtrLSB, 3);
788cfca06d7SDimitry Andric } else {
789cfca06d7SDimitry Andric // turn bytes into bits, and count from the other side.
790cfca06d7SDimitry Andric PMV.ShiftAmt = Builder.CreateShl(
791cfca06d7SDimitry Andric Builder.CreateXor(PtrLSB, MinWordSize - ValueSize), 3);
792cfca06d7SDimitry Andric }
793cfca06d7SDimitry Andric
794cfca06d7SDimitry Andric PMV.ShiftAmt = Builder.CreateTrunc(PMV.ShiftAmt, PMV.WordType, "ShiftAmt");
795cfca06d7SDimitry Andric PMV.Mask = Builder.CreateShl(
796cfca06d7SDimitry Andric ConstantInt::get(PMV.WordType, (1 << (ValueSize * 8)) - 1), PMV.ShiftAmt,
797cfca06d7SDimitry Andric "Mask");
798e3b55780SDimitry Andric
799cfca06d7SDimitry Andric PMV.Inv_Mask = Builder.CreateNot(PMV.Mask, "Inv_Mask");
800e3b55780SDimitry Andric
801cfca06d7SDimitry Andric return PMV;
802cfca06d7SDimitry Andric }
803cfca06d7SDimitry Andric
extractMaskedValue(IRBuilderBase & Builder,Value * WideWord,const PartwordMaskValues & PMV)804e3b55780SDimitry Andric static Value *extractMaskedValue(IRBuilderBase &Builder, Value *WideWord,
805cfca06d7SDimitry Andric const PartwordMaskValues &PMV) {
806cfca06d7SDimitry Andric assert(WideWord->getType() == PMV.WordType && "Widened type mismatch");
807cfca06d7SDimitry Andric if (PMV.WordType == PMV.ValueType)
808cfca06d7SDimitry Andric return WideWord;
809cfca06d7SDimitry Andric
810cfca06d7SDimitry Andric Value *Shift = Builder.CreateLShr(WideWord, PMV.ShiftAmt, "shifted");
811e3b55780SDimitry Andric Value *Trunc = Builder.CreateTrunc(Shift, PMV.IntValueType, "extracted");
812e3b55780SDimitry Andric return Builder.CreateBitCast(Trunc, PMV.ValueType);
813cfca06d7SDimitry Andric }
814cfca06d7SDimitry Andric
insertMaskedValue(IRBuilderBase & Builder,Value * WideWord,Value * Updated,const PartwordMaskValues & PMV)815e3b55780SDimitry Andric static Value *insertMaskedValue(IRBuilderBase &Builder, Value *WideWord,
816cfca06d7SDimitry Andric Value *Updated, const PartwordMaskValues &PMV) {
817cfca06d7SDimitry Andric assert(WideWord->getType() == PMV.WordType && "Widened type mismatch");
818cfca06d7SDimitry Andric assert(Updated->getType() == PMV.ValueType && "Value type mismatch");
819cfca06d7SDimitry Andric if (PMV.WordType == PMV.ValueType)
820cfca06d7SDimitry Andric return Updated;
821cfca06d7SDimitry Andric
822e3b55780SDimitry Andric Updated = Builder.CreateBitCast(Updated, PMV.IntValueType);
823e3b55780SDimitry Andric
824cfca06d7SDimitry Andric Value *ZExt = Builder.CreateZExt(Updated, PMV.WordType, "extended");
825cfca06d7SDimitry Andric Value *Shift =
826cfca06d7SDimitry Andric Builder.CreateShl(ZExt, PMV.ShiftAmt, "shifted", /*HasNUW*/ true);
827cfca06d7SDimitry Andric Value *And = Builder.CreateAnd(WideWord, PMV.Inv_Mask, "unmasked");
828cfca06d7SDimitry Andric Value *Or = Builder.CreateOr(And, Shift, "inserted");
829cfca06d7SDimitry Andric return Or;
83001095a5dSDimitry Andric }
83101095a5dSDimitry Andric
83201095a5dSDimitry Andric /// Emit IR to implement a masked version of a given atomicrmw
83301095a5dSDimitry Andric /// operation. (That is, only the bits under the Mask should be
83401095a5dSDimitry Andric /// affected by the operation)
performMaskedAtomicOp(AtomicRMWInst::BinOp Op,IRBuilderBase & Builder,Value * Loaded,Value * Shifted_Inc,Value * Inc,const PartwordMaskValues & PMV)83501095a5dSDimitry Andric static Value *performMaskedAtomicOp(AtomicRMWInst::BinOp Op,
836e3b55780SDimitry Andric IRBuilderBase &Builder, Value *Loaded,
83701095a5dSDimitry Andric Value *Shifted_Inc, Value *Inc,
83801095a5dSDimitry Andric const PartwordMaskValues &PMV) {
839d8e91e46SDimitry Andric // TODO: update to use
840d8e91e46SDimitry Andric // https://graphics.stanford.edu/~seander/bithacks.html#MaskedMerge in order
841d8e91e46SDimitry Andric // to merge bits from two values without requiring PMV.Inv_Mask.
84201095a5dSDimitry Andric switch (Op) {
84301095a5dSDimitry Andric case AtomicRMWInst::Xchg: {
84401095a5dSDimitry Andric Value *Loaded_MaskOut = Builder.CreateAnd(Loaded, PMV.Inv_Mask);
84501095a5dSDimitry Andric Value *FinalVal = Builder.CreateOr(Loaded_MaskOut, Shifted_Inc);
84601095a5dSDimitry Andric return FinalVal;
84701095a5dSDimitry Andric }
84801095a5dSDimitry Andric case AtomicRMWInst::Or:
84901095a5dSDimitry Andric case AtomicRMWInst::Xor:
850d8e91e46SDimitry Andric case AtomicRMWInst::And:
851d8e91e46SDimitry Andric llvm_unreachable("Or/Xor/And handled by widenPartwordAtomicRMW");
85201095a5dSDimitry Andric case AtomicRMWInst::Add:
85301095a5dSDimitry Andric case AtomicRMWInst::Sub:
85401095a5dSDimitry Andric case AtomicRMWInst::Nand: {
85501095a5dSDimitry Andric // The other arithmetic ops need to be masked into place.
856145449b1SDimitry Andric Value *NewVal = buildAtomicRMWValue(Op, Builder, Loaded, Shifted_Inc);
85701095a5dSDimitry Andric Value *NewVal_Masked = Builder.CreateAnd(NewVal, PMV.Mask);
85801095a5dSDimitry Andric Value *Loaded_MaskOut = Builder.CreateAnd(Loaded, PMV.Inv_Mask);
85901095a5dSDimitry Andric Value *FinalVal = Builder.CreateOr(Loaded_MaskOut, NewVal_Masked);
86001095a5dSDimitry Andric return FinalVal;
86101095a5dSDimitry Andric }
86201095a5dSDimitry Andric case AtomicRMWInst::Max:
86301095a5dSDimitry Andric case AtomicRMWInst::Min:
86401095a5dSDimitry Andric case AtomicRMWInst::UMax:
865e3b55780SDimitry Andric case AtomicRMWInst::UMin:
866e3b55780SDimitry Andric case AtomicRMWInst::FAdd:
867e3b55780SDimitry Andric case AtomicRMWInst::FSub:
868e3b55780SDimitry Andric case AtomicRMWInst::FMin:
869e3b55780SDimitry Andric case AtomicRMWInst::FMax:
870e3b55780SDimitry Andric case AtomicRMWInst::UIncWrap:
871e3b55780SDimitry Andric case AtomicRMWInst::UDecWrap: {
872e3b55780SDimitry Andric // Finally, other ops will operate on the full value, so truncate down to
873e3b55780SDimitry Andric // the original size, and expand out again after doing the
874e3b55780SDimitry Andric // operation. Bitcasts will be inserted for FP values.
875cfca06d7SDimitry Andric Value *Loaded_Extract = extractMaskedValue(Builder, Loaded, PMV);
876145449b1SDimitry Andric Value *NewVal = buildAtomicRMWValue(Op, Builder, Loaded_Extract, Inc);
877cfca06d7SDimitry Andric Value *FinalVal = insertMaskedValue(Builder, Loaded, NewVal, PMV);
87801095a5dSDimitry Andric return FinalVal;
87901095a5dSDimitry Andric }
88001095a5dSDimitry Andric default:
88101095a5dSDimitry Andric llvm_unreachable("Unknown atomic op");
88201095a5dSDimitry Andric }
88301095a5dSDimitry Andric }
88401095a5dSDimitry Andric
88501095a5dSDimitry Andric /// Expand a sub-word atomicrmw operation into an appropriate
88601095a5dSDimitry Andric /// word-sized operation.
88701095a5dSDimitry Andric ///
88801095a5dSDimitry Andric /// It will create an LL/SC or cmpxchg loop, as appropriate, the same
88901095a5dSDimitry Andric /// way as a typical atomicrmw expansion. The only difference here is
890cfca06d7SDimitry Andric /// that the operation inside of the loop may operate upon only a
89101095a5dSDimitry Andric /// part of the value.
expandPartwordAtomicRMW(AtomicRMWInst * AI,TargetLoweringBase::AtomicExpansionKind ExpansionKind)892ac9a064cSDimitry Andric void AtomicExpandImpl::expandPartwordAtomicRMW(
89301095a5dSDimitry Andric AtomicRMWInst *AI, TargetLoweringBase::AtomicExpansionKind ExpansionKind) {
894ac9a064cSDimitry Andric // Widen And/Or/Xor and give the target another chance at expanding it.
895ac9a064cSDimitry Andric AtomicRMWInst::BinOp Op = AI->getOperation();
896ac9a064cSDimitry Andric if (Op == AtomicRMWInst::Or || Op == AtomicRMWInst::Xor ||
897ac9a064cSDimitry Andric Op == AtomicRMWInst::And) {
898ac9a064cSDimitry Andric tryExpandAtomicRMW(widenPartwordAtomicRMW(AI));
899ac9a064cSDimitry Andric return;
900ac9a064cSDimitry Andric }
90101095a5dSDimitry Andric AtomicOrdering MemOpOrder = AI->getOrdering();
902344a3780SDimitry Andric SyncScope::ID SSID = AI->getSyncScopeID();
90301095a5dSDimitry Andric
904e3b55780SDimitry Andric ReplacementIRBuilder Builder(AI, *DL);
90501095a5dSDimitry Andric
90601095a5dSDimitry Andric PartwordMaskValues PMV =
90701095a5dSDimitry Andric createMaskInstrs(Builder, AI, AI->getType(), AI->getPointerOperand(),
908344a3780SDimitry Andric AI->getAlign(), TLI->getMinCmpXchgSizeInBits() / 8);
90901095a5dSDimitry Andric
910e3b55780SDimitry Andric Value *ValOperand_Shifted = nullptr;
911ac9a064cSDimitry Andric if (Op == AtomicRMWInst::Xchg || Op == AtomicRMWInst::Add ||
912ac9a064cSDimitry Andric Op == AtomicRMWInst::Sub || Op == AtomicRMWInst::Nand) {
913ac9a064cSDimitry Andric Value *ValOp = Builder.CreateBitCast(AI->getValOperand(), PMV.IntValueType);
914e3b55780SDimitry Andric ValOperand_Shifted =
915ac9a064cSDimitry Andric Builder.CreateShl(Builder.CreateZExt(ValOp, PMV.WordType), PMV.ShiftAmt,
916ac9a064cSDimitry Andric "ValOperand_Shifted");
917e3b55780SDimitry Andric }
91801095a5dSDimitry Andric
919e3b55780SDimitry Andric auto PerformPartwordOp = [&](IRBuilderBase &Builder, Value *Loaded) {
920ac9a064cSDimitry Andric return performMaskedAtomicOp(Op, Builder, Loaded, ValOperand_Shifted,
921ac9a064cSDimitry Andric AI->getValOperand(), PMV);
92201095a5dSDimitry Andric };
92301095a5dSDimitry Andric
924cfca06d7SDimitry Andric Value *OldResult;
925cfca06d7SDimitry Andric if (ExpansionKind == TargetLoweringBase::AtomicExpansionKind::CmpXChg) {
926344a3780SDimitry Andric OldResult = insertRMWCmpXchgLoop(Builder, PMV.WordType, PMV.AlignedAddr,
927145449b1SDimitry Andric PMV.AlignedAddrAlignment, MemOpOrder, SSID,
928145449b1SDimitry Andric PerformPartwordOp, createCmpXchgInstFun);
929cfca06d7SDimitry Andric } else {
930cfca06d7SDimitry Andric assert(ExpansionKind == TargetLoweringBase::AtomicExpansionKind::LLSC);
931cfca06d7SDimitry Andric OldResult = insertRMWLLSCLoop(Builder, PMV.WordType, PMV.AlignedAddr,
932344a3780SDimitry Andric PMV.AlignedAddrAlignment, MemOpOrder,
933344a3780SDimitry Andric PerformPartwordOp);
934cfca06d7SDimitry Andric }
935cfca06d7SDimitry Andric
936cfca06d7SDimitry Andric Value *FinalOldResult = extractMaskedValue(Builder, OldResult, PMV);
93701095a5dSDimitry Andric AI->replaceAllUsesWith(FinalOldResult);
93801095a5dSDimitry Andric AI->eraseFromParent();
93901095a5dSDimitry Andric }
94001095a5dSDimitry Andric
941ac9a064cSDimitry Andric /// Copy metadata that's safe to preserve when widening atomics.
copyMetadataForAtomic(Instruction & Dest,const Instruction & Source)942ac9a064cSDimitry Andric static void copyMetadataForAtomic(Instruction &Dest,
943ac9a064cSDimitry Andric const Instruction &Source) {
944ac9a064cSDimitry Andric SmallVector<std::pair<unsigned, MDNode *>, 8> MD;
945ac9a064cSDimitry Andric Source.getAllMetadata(MD);
946ac9a064cSDimitry Andric LLVMContext &Ctx = Dest.getContext();
947ac9a064cSDimitry Andric MDBuilder MDB(Ctx);
948ac9a064cSDimitry Andric
949ac9a064cSDimitry Andric for (auto [ID, N] : MD) {
950ac9a064cSDimitry Andric switch (ID) {
951ac9a064cSDimitry Andric case LLVMContext::MD_dbg:
952ac9a064cSDimitry Andric case LLVMContext::MD_tbaa:
953ac9a064cSDimitry Andric case LLVMContext::MD_tbaa_struct:
954ac9a064cSDimitry Andric case LLVMContext::MD_alias_scope:
955ac9a064cSDimitry Andric case LLVMContext::MD_noalias:
956ac9a064cSDimitry Andric case LLVMContext::MD_access_group:
957ac9a064cSDimitry Andric case LLVMContext::MD_mmra:
958ac9a064cSDimitry Andric Dest.setMetadata(ID, N);
959ac9a064cSDimitry Andric break;
960ac9a064cSDimitry Andric default:
961ac9a064cSDimitry Andric if (ID == Ctx.getMDKindID("amdgpu.no.remote.memory"))
962ac9a064cSDimitry Andric Dest.setMetadata(ID, N);
963ac9a064cSDimitry Andric else if (ID == Ctx.getMDKindID("amdgpu.no.fine.grained.memory"))
964ac9a064cSDimitry Andric Dest.setMetadata(ID, N);
965ac9a064cSDimitry Andric
966ac9a064cSDimitry Andric break;
967ac9a064cSDimitry Andric }
968ac9a064cSDimitry Andric }
969ac9a064cSDimitry Andric }
970ac9a064cSDimitry Andric
971d8e91e46SDimitry Andric // Widen the bitwise atomicrmw (or/xor/and) to the minimum supported width.
widenPartwordAtomicRMW(AtomicRMWInst * AI)972ac9a064cSDimitry Andric AtomicRMWInst *AtomicExpandImpl::widenPartwordAtomicRMW(AtomicRMWInst *AI) {
973e3b55780SDimitry Andric ReplacementIRBuilder Builder(AI, *DL);
974d8e91e46SDimitry Andric AtomicRMWInst::BinOp Op = AI->getOperation();
975d8e91e46SDimitry Andric
976d8e91e46SDimitry Andric assert((Op == AtomicRMWInst::Or || Op == AtomicRMWInst::Xor ||
977d8e91e46SDimitry Andric Op == AtomicRMWInst::And) &&
978d8e91e46SDimitry Andric "Unable to widen operation");
979d8e91e46SDimitry Andric
980d8e91e46SDimitry Andric PartwordMaskValues PMV =
981d8e91e46SDimitry Andric createMaskInstrs(Builder, AI, AI->getType(), AI->getPointerOperand(),
982344a3780SDimitry Andric AI->getAlign(), TLI->getMinCmpXchgSizeInBits() / 8);
983d8e91e46SDimitry Andric
984d8e91e46SDimitry Andric Value *ValOperand_Shifted =
985d8e91e46SDimitry Andric Builder.CreateShl(Builder.CreateZExt(AI->getValOperand(), PMV.WordType),
986d8e91e46SDimitry Andric PMV.ShiftAmt, "ValOperand_Shifted");
987d8e91e46SDimitry Andric
988d8e91e46SDimitry Andric Value *NewOperand;
989d8e91e46SDimitry Andric
990d8e91e46SDimitry Andric if (Op == AtomicRMWInst::And)
991d8e91e46SDimitry Andric NewOperand =
992ac9a064cSDimitry Andric Builder.CreateOr(ValOperand_Shifted, PMV.Inv_Mask, "AndOperand");
993d8e91e46SDimitry Andric else
994d8e91e46SDimitry Andric NewOperand = ValOperand_Shifted;
995d8e91e46SDimitry Andric
996b1c73532SDimitry Andric AtomicRMWInst *NewAI = Builder.CreateAtomicRMW(
997b1c73532SDimitry Andric Op, PMV.AlignedAddr, NewOperand, PMV.AlignedAddrAlignment,
998b1c73532SDimitry Andric AI->getOrdering(), AI->getSyncScopeID());
999ac9a064cSDimitry Andric
1000ac9a064cSDimitry Andric copyMetadataForAtomic(*NewAI, *AI);
1001d8e91e46SDimitry Andric
1002cfca06d7SDimitry Andric Value *FinalOldResult = extractMaskedValue(Builder, NewAI, PMV);
1003d8e91e46SDimitry Andric AI->replaceAllUsesWith(FinalOldResult);
1004d8e91e46SDimitry Andric AI->eraseFromParent();
1005d8e91e46SDimitry Andric return NewAI;
1006d8e91e46SDimitry Andric }
1007d8e91e46SDimitry Andric
expandPartwordCmpXchg(AtomicCmpXchgInst * CI)1008ac9a064cSDimitry Andric bool AtomicExpandImpl::expandPartwordCmpXchg(AtomicCmpXchgInst *CI) {
100901095a5dSDimitry Andric // The basic idea here is that we're expanding a cmpxchg of a
101001095a5dSDimitry Andric // smaller memory size up to a word-sized cmpxchg. To do this, we
101101095a5dSDimitry Andric // need to add a retry-loop for strong cmpxchg, so that
101201095a5dSDimitry Andric // modifications to other parts of the word don't cause a spurious
101301095a5dSDimitry Andric // failure.
101401095a5dSDimitry Andric
101501095a5dSDimitry Andric // This generates code like the following:
101601095a5dSDimitry Andric // [[Setup mask values PMV.*]]
101701095a5dSDimitry Andric // %NewVal_Shifted = shl i32 %NewVal, %PMV.ShiftAmt
101801095a5dSDimitry Andric // %Cmp_Shifted = shl i32 %Cmp, %PMV.ShiftAmt
101901095a5dSDimitry Andric // %InitLoaded = load i32* %addr
102001095a5dSDimitry Andric // %InitLoaded_MaskOut = and i32 %InitLoaded, %PMV.Inv_Mask
102101095a5dSDimitry Andric // br partword.cmpxchg.loop
102201095a5dSDimitry Andric // partword.cmpxchg.loop:
102301095a5dSDimitry Andric // %Loaded_MaskOut = phi i32 [ %InitLoaded_MaskOut, %entry ],
102401095a5dSDimitry Andric // [ %OldVal_MaskOut, %partword.cmpxchg.failure ]
102501095a5dSDimitry Andric // %FullWord_NewVal = or i32 %Loaded_MaskOut, %NewVal_Shifted
102601095a5dSDimitry Andric // %FullWord_Cmp = or i32 %Loaded_MaskOut, %Cmp_Shifted
102701095a5dSDimitry Andric // %NewCI = cmpxchg i32* %PMV.AlignedAddr, i32 %FullWord_Cmp,
102801095a5dSDimitry Andric // i32 %FullWord_NewVal success_ordering failure_ordering
102901095a5dSDimitry Andric // %OldVal = extractvalue { i32, i1 } %NewCI, 0
103001095a5dSDimitry Andric // %Success = extractvalue { i32, i1 } %NewCI, 1
103101095a5dSDimitry Andric // br i1 %Success, label %partword.cmpxchg.end,
103201095a5dSDimitry Andric // label %partword.cmpxchg.failure
103301095a5dSDimitry Andric // partword.cmpxchg.failure:
103401095a5dSDimitry Andric // %OldVal_MaskOut = and i32 %OldVal, %PMV.Inv_Mask
103501095a5dSDimitry Andric // %ShouldContinue = icmp ne i32 %Loaded_MaskOut, %OldVal_MaskOut
103601095a5dSDimitry Andric // br i1 %ShouldContinue, label %partword.cmpxchg.loop,
103701095a5dSDimitry Andric // label %partword.cmpxchg.end
103801095a5dSDimitry Andric // partword.cmpxchg.end:
103901095a5dSDimitry Andric // %tmp1 = lshr i32 %OldVal, %PMV.ShiftAmt
104001095a5dSDimitry Andric // %FinalOldVal = trunc i32 %tmp1 to i8
104101095a5dSDimitry Andric // %tmp2 = insertvalue { i8, i1 } undef, i8 %FinalOldVal, 0
104201095a5dSDimitry Andric // %Res = insertvalue { i8, i1 } %25, i1 %Success, 1
104301095a5dSDimitry Andric
104401095a5dSDimitry Andric Value *Addr = CI->getPointerOperand();
104501095a5dSDimitry Andric Value *Cmp = CI->getCompareOperand();
104601095a5dSDimitry Andric Value *NewVal = CI->getNewValOperand();
104701095a5dSDimitry Andric
104801095a5dSDimitry Andric BasicBlock *BB = CI->getParent();
104901095a5dSDimitry Andric Function *F = BB->getParent();
1050e3b55780SDimitry Andric ReplacementIRBuilder Builder(CI, *DL);
105101095a5dSDimitry Andric LLVMContext &Ctx = Builder.getContext();
105201095a5dSDimitry Andric
105301095a5dSDimitry Andric BasicBlock *EndBB =
105401095a5dSDimitry Andric BB->splitBasicBlock(CI->getIterator(), "partword.cmpxchg.end");
105501095a5dSDimitry Andric auto FailureBB =
105601095a5dSDimitry Andric BasicBlock::Create(Ctx, "partword.cmpxchg.failure", F, EndBB);
105701095a5dSDimitry Andric auto LoopBB = BasicBlock::Create(Ctx, "partword.cmpxchg.loop", F, FailureBB);
105801095a5dSDimitry Andric
105901095a5dSDimitry Andric // The split call above "helpfully" added a branch at the end of BB
106001095a5dSDimitry Andric // (to the wrong place).
106101095a5dSDimitry Andric std::prev(BB->end())->eraseFromParent();
106201095a5dSDimitry Andric Builder.SetInsertPoint(BB);
106301095a5dSDimitry Andric
1064344a3780SDimitry Andric PartwordMaskValues PMV =
1065344a3780SDimitry Andric createMaskInstrs(Builder, CI, CI->getCompareOperand()->getType(), Addr,
1066344a3780SDimitry Andric CI->getAlign(), TLI->getMinCmpXchgSizeInBits() / 8);
106701095a5dSDimitry Andric
106801095a5dSDimitry Andric // Shift the incoming values over, into the right location in the word.
106901095a5dSDimitry Andric Value *NewVal_Shifted =
107001095a5dSDimitry Andric Builder.CreateShl(Builder.CreateZExt(NewVal, PMV.WordType), PMV.ShiftAmt);
107101095a5dSDimitry Andric Value *Cmp_Shifted =
107201095a5dSDimitry Andric Builder.CreateShl(Builder.CreateZExt(Cmp, PMV.WordType), PMV.ShiftAmt);
107301095a5dSDimitry Andric
107401095a5dSDimitry Andric // Load the entire current word, and mask into place the expected and new
107501095a5dSDimitry Andric // values
107601095a5dSDimitry Andric LoadInst *InitLoaded = Builder.CreateLoad(PMV.WordType, PMV.AlignedAddr);
107701095a5dSDimitry Andric InitLoaded->setVolatile(CI->isVolatile());
107801095a5dSDimitry Andric Value *InitLoaded_MaskOut = Builder.CreateAnd(InitLoaded, PMV.Inv_Mask);
107901095a5dSDimitry Andric Builder.CreateBr(LoopBB);
108001095a5dSDimitry Andric
108101095a5dSDimitry Andric // partword.cmpxchg.loop:
108201095a5dSDimitry Andric Builder.SetInsertPoint(LoopBB);
108301095a5dSDimitry Andric PHINode *Loaded_MaskOut = Builder.CreatePHI(PMV.WordType, 2);
108401095a5dSDimitry Andric Loaded_MaskOut->addIncoming(InitLoaded_MaskOut, BB);
108501095a5dSDimitry Andric
108601095a5dSDimitry Andric // Mask/Or the expected and new values into place in the loaded word.
108701095a5dSDimitry Andric Value *FullWord_NewVal = Builder.CreateOr(Loaded_MaskOut, NewVal_Shifted);
108801095a5dSDimitry Andric Value *FullWord_Cmp = Builder.CreateOr(Loaded_MaskOut, Cmp_Shifted);
108901095a5dSDimitry Andric AtomicCmpXchgInst *NewCI = Builder.CreateAtomicCmpXchg(
1090344a3780SDimitry Andric PMV.AlignedAddr, FullWord_Cmp, FullWord_NewVal, PMV.AlignedAddrAlignment,
1091344a3780SDimitry Andric CI->getSuccessOrdering(), CI->getFailureOrdering(), CI->getSyncScopeID());
109201095a5dSDimitry Andric NewCI->setVolatile(CI->isVolatile());
109301095a5dSDimitry Andric // When we're building a strong cmpxchg, we need a loop, so you
109401095a5dSDimitry Andric // might think we could use a weak cmpxchg inside. But, using strong
109501095a5dSDimitry Andric // allows the below comparison for ShouldContinue, and we're
109601095a5dSDimitry Andric // expecting the underlying cmpxchg to be a machine instruction,
109701095a5dSDimitry Andric // which is strong anyways.
109801095a5dSDimitry Andric NewCI->setWeak(CI->isWeak());
109901095a5dSDimitry Andric
110001095a5dSDimitry Andric Value *OldVal = Builder.CreateExtractValue(NewCI, 0);
110101095a5dSDimitry Andric Value *Success = Builder.CreateExtractValue(NewCI, 1);
110201095a5dSDimitry Andric
110301095a5dSDimitry Andric if (CI->isWeak())
110401095a5dSDimitry Andric Builder.CreateBr(EndBB);
110501095a5dSDimitry Andric else
110601095a5dSDimitry Andric Builder.CreateCondBr(Success, EndBB, FailureBB);
110701095a5dSDimitry Andric
110801095a5dSDimitry Andric // partword.cmpxchg.failure:
110901095a5dSDimitry Andric Builder.SetInsertPoint(FailureBB);
111001095a5dSDimitry Andric // Upon failure, verify that the masked-out part of the loaded value
111101095a5dSDimitry Andric // has been modified. If it didn't, abort the cmpxchg, since the
111201095a5dSDimitry Andric // masked-in part must've.
111301095a5dSDimitry Andric Value *OldVal_MaskOut = Builder.CreateAnd(OldVal, PMV.Inv_Mask);
111401095a5dSDimitry Andric Value *ShouldContinue = Builder.CreateICmpNE(Loaded_MaskOut, OldVal_MaskOut);
111501095a5dSDimitry Andric Builder.CreateCondBr(ShouldContinue, LoopBB, EndBB);
111601095a5dSDimitry Andric
111701095a5dSDimitry Andric // Add the second value to the phi from above
111801095a5dSDimitry Andric Loaded_MaskOut->addIncoming(OldVal_MaskOut, FailureBB);
111901095a5dSDimitry Andric
112001095a5dSDimitry Andric // partword.cmpxchg.end:
112101095a5dSDimitry Andric Builder.SetInsertPoint(CI);
112201095a5dSDimitry Andric
1123cfca06d7SDimitry Andric Value *FinalOldVal = extractMaskedValue(Builder, OldVal, PMV);
1124e3b55780SDimitry Andric Value *Res = PoisonValue::get(CI->getType());
112501095a5dSDimitry Andric Res = Builder.CreateInsertValue(Res, FinalOldVal, 0);
112601095a5dSDimitry Andric Res = Builder.CreateInsertValue(Res, Success, 1);
112701095a5dSDimitry Andric
112801095a5dSDimitry Andric CI->replaceAllUsesWith(Res);
112901095a5dSDimitry Andric CI->eraseFromParent();
1130cfca06d7SDimitry Andric return true;
113101095a5dSDimitry Andric }
113201095a5dSDimitry Andric
expandAtomicOpToLLSC(Instruction * I,Type * ResultType,Value * Addr,Align AddrAlign,AtomicOrdering MemOpOrder,function_ref<Value * (IRBuilderBase &,Value *)> PerformOp)1133ac9a064cSDimitry Andric void AtomicExpandImpl::expandAtomicOpToLLSC(
1134344a3780SDimitry Andric Instruction *I, Type *ResultType, Value *Addr, Align AddrAlign,
1135344a3780SDimitry Andric AtomicOrdering MemOpOrder,
1136e3b55780SDimitry Andric function_ref<Value *(IRBuilderBase &, Value *)> PerformOp) {
1137e3b55780SDimitry Andric ReplacementIRBuilder Builder(I, *DL);
1138344a3780SDimitry Andric Value *Loaded = insertRMWLLSCLoop(Builder, ResultType, Addr, AddrAlign,
1139344a3780SDimitry Andric MemOpOrder, PerformOp);
114001095a5dSDimitry Andric
114101095a5dSDimitry Andric I->replaceAllUsesWith(Loaded);
114201095a5dSDimitry Andric I->eraseFromParent();
114301095a5dSDimitry Andric }
114401095a5dSDimitry Andric
expandAtomicRMWToMaskedIntrinsic(AtomicRMWInst * AI)1145ac9a064cSDimitry Andric void AtomicExpandImpl::expandAtomicRMWToMaskedIntrinsic(AtomicRMWInst *AI) {
1146e3b55780SDimitry Andric ReplacementIRBuilder Builder(AI, *DL);
1147d8e91e46SDimitry Andric
1148d8e91e46SDimitry Andric PartwordMaskValues PMV =
1149d8e91e46SDimitry Andric createMaskInstrs(Builder, AI, AI->getType(), AI->getPointerOperand(),
1150344a3780SDimitry Andric AI->getAlign(), TLI->getMinCmpXchgSizeInBits() / 8);
1151d8e91e46SDimitry Andric
1152d8e91e46SDimitry Andric // The value operand must be sign-extended for signed min/max so that the
1153d8e91e46SDimitry Andric // target's signed comparison instructions can be used. Otherwise, just
1154d8e91e46SDimitry Andric // zero-ext.
1155d8e91e46SDimitry Andric Instruction::CastOps CastOp = Instruction::ZExt;
1156d8e91e46SDimitry Andric AtomicRMWInst::BinOp RMWOp = AI->getOperation();
1157d8e91e46SDimitry Andric if (RMWOp == AtomicRMWInst::Max || RMWOp == AtomicRMWInst::Min)
1158d8e91e46SDimitry Andric CastOp = Instruction::SExt;
1159d8e91e46SDimitry Andric
1160d8e91e46SDimitry Andric Value *ValOperand_Shifted = Builder.CreateShl(
1161d8e91e46SDimitry Andric Builder.CreateCast(CastOp, AI->getValOperand(), PMV.WordType),
1162d8e91e46SDimitry Andric PMV.ShiftAmt, "ValOperand_Shifted");
1163d8e91e46SDimitry Andric Value *OldResult = TLI->emitMaskedAtomicRMWIntrinsic(
1164d8e91e46SDimitry Andric Builder, AI, PMV.AlignedAddr, ValOperand_Shifted, PMV.Mask, PMV.ShiftAmt,
1165d8e91e46SDimitry Andric AI->getOrdering());
1166cfca06d7SDimitry Andric Value *FinalOldResult = extractMaskedValue(Builder, OldResult, PMV);
1167d8e91e46SDimitry Andric AI->replaceAllUsesWith(FinalOldResult);
1168d8e91e46SDimitry Andric AI->eraseFromParent();
1169d8e91e46SDimitry Andric }
1170d8e91e46SDimitry Andric
expandAtomicCmpXchgToMaskedIntrinsic(AtomicCmpXchgInst * CI)1171ac9a064cSDimitry Andric void AtomicExpandImpl::expandAtomicCmpXchgToMaskedIntrinsic(
1172ac9a064cSDimitry Andric AtomicCmpXchgInst *CI) {
1173e3b55780SDimitry Andric ReplacementIRBuilder Builder(CI, *DL);
1174d8e91e46SDimitry Andric
1175d8e91e46SDimitry Andric PartwordMaskValues PMV = createMaskInstrs(
1176d8e91e46SDimitry Andric Builder, CI, CI->getCompareOperand()->getType(), CI->getPointerOperand(),
1177344a3780SDimitry Andric CI->getAlign(), TLI->getMinCmpXchgSizeInBits() / 8);
1178d8e91e46SDimitry Andric
1179d8e91e46SDimitry Andric Value *CmpVal_Shifted = Builder.CreateShl(
1180d8e91e46SDimitry Andric Builder.CreateZExt(CI->getCompareOperand(), PMV.WordType), PMV.ShiftAmt,
1181d8e91e46SDimitry Andric "CmpVal_Shifted");
1182d8e91e46SDimitry Andric Value *NewVal_Shifted = Builder.CreateShl(
1183d8e91e46SDimitry Andric Builder.CreateZExt(CI->getNewValOperand(), PMV.WordType), PMV.ShiftAmt,
1184d8e91e46SDimitry Andric "NewVal_Shifted");
1185d8e91e46SDimitry Andric Value *OldVal = TLI->emitMaskedAtomicCmpXchgIntrinsic(
1186d8e91e46SDimitry Andric Builder, CI, PMV.AlignedAddr, CmpVal_Shifted, NewVal_Shifted, PMV.Mask,
1187344a3780SDimitry Andric CI->getMergedOrdering());
1188cfca06d7SDimitry Andric Value *FinalOldVal = extractMaskedValue(Builder, OldVal, PMV);
1189e3b55780SDimitry Andric Value *Res = PoisonValue::get(CI->getType());
1190d8e91e46SDimitry Andric Res = Builder.CreateInsertValue(Res, FinalOldVal, 0);
1191d8e91e46SDimitry Andric Value *Success = Builder.CreateICmpEQ(
1192d8e91e46SDimitry Andric CmpVal_Shifted, Builder.CreateAnd(OldVal, PMV.Mask), "Success");
1193d8e91e46SDimitry Andric Res = Builder.CreateInsertValue(Res, Success, 1);
1194d8e91e46SDimitry Andric
1195d8e91e46SDimitry Andric CI->replaceAllUsesWith(Res);
1196d8e91e46SDimitry Andric CI->eraseFromParent();
1197d8e91e46SDimitry Andric }
1198d8e91e46SDimitry Andric
insertRMWLLSCLoop(IRBuilderBase & Builder,Type * ResultTy,Value * Addr,Align AddrAlign,AtomicOrdering MemOpOrder,function_ref<Value * (IRBuilderBase &,Value *)> PerformOp)1199ac9a064cSDimitry Andric Value *AtomicExpandImpl::insertRMWLLSCLoop(
1200e3b55780SDimitry Andric IRBuilderBase &Builder, Type *ResultTy, Value *Addr, Align AddrAlign,
120101095a5dSDimitry Andric AtomicOrdering MemOpOrder,
1202e3b55780SDimitry Andric function_ref<Value *(IRBuilderBase &, Value *)> PerformOp) {
120301095a5dSDimitry Andric LLVMContext &Ctx = Builder.getContext();
120401095a5dSDimitry Andric BasicBlock *BB = Builder.GetInsertBlock();
120501095a5dSDimitry Andric Function *F = BB->getParent();
120667c32a98SDimitry Andric
1207344a3780SDimitry Andric assert(AddrAlign >=
1208ac9a064cSDimitry Andric F->getDataLayout().getTypeStoreSize(ResultTy) &&
1209344a3780SDimitry Andric "Expected at least natural alignment at this point.");
1210344a3780SDimitry Andric
121167c32a98SDimitry Andric // Given: atomicrmw some_op iN* %addr, iN %incr ordering
121267c32a98SDimitry Andric //
121367c32a98SDimitry Andric // The standard expansion we produce is:
121467c32a98SDimitry Andric // [...]
121567c32a98SDimitry Andric // atomicrmw.start:
121667c32a98SDimitry Andric // %loaded = @load.linked(%addr)
121767c32a98SDimitry Andric // %new = some_op iN %loaded, %incr
121867c32a98SDimitry Andric // %stored = @store_conditional(%new, %addr)
121967c32a98SDimitry Andric // %try_again = icmp i32 ne %stored, 0
122067c32a98SDimitry Andric // br i1 %try_again, label %loop, label %atomicrmw.end
122167c32a98SDimitry Andric // atomicrmw.end:
122267c32a98SDimitry Andric // [...]
122301095a5dSDimitry Andric BasicBlock *ExitBB =
122401095a5dSDimitry Andric BB->splitBasicBlock(Builder.GetInsertPoint(), "atomicrmw.end");
122567c32a98SDimitry Andric BasicBlock *LoopBB = BasicBlock::Create(Ctx, "atomicrmw.start", F, ExitBB);
122667c32a98SDimitry Andric
122767c32a98SDimitry Andric // The split call above "helpfully" added a branch at the end of BB (to the
122801095a5dSDimitry Andric // wrong place).
122967c32a98SDimitry Andric std::prev(BB->end())->eraseFromParent();
123067c32a98SDimitry Andric Builder.SetInsertPoint(BB);
123167c32a98SDimitry Andric Builder.CreateBr(LoopBB);
123267c32a98SDimitry Andric
123367c32a98SDimitry Andric // Start the main loop block now that we've taken care of the preliminaries.
123467c32a98SDimitry Andric Builder.SetInsertPoint(LoopBB);
1235344a3780SDimitry Andric Value *Loaded = TLI->emitLoadLinked(Builder, ResultTy, Addr, MemOpOrder);
123667c32a98SDimitry Andric
1237dd58ef01SDimitry Andric Value *NewVal = PerformOp(Builder, Loaded);
123867c32a98SDimitry Andric
123967c32a98SDimitry Andric Value *StoreSuccess =
124067c32a98SDimitry Andric TLI->emitStoreConditional(Builder, NewVal, Addr, MemOpOrder);
124167c32a98SDimitry Andric Value *TryAgain = Builder.CreateICmpNE(
124267c32a98SDimitry Andric StoreSuccess, ConstantInt::get(IntegerType::get(Ctx, 32), 0), "tryagain");
124367c32a98SDimitry Andric Builder.CreateCondBr(TryAgain, LoopBB, ExitBB);
124467c32a98SDimitry Andric
124567c32a98SDimitry Andric Builder.SetInsertPoint(ExitBB, ExitBB->begin());
124601095a5dSDimitry Andric return Loaded;
124767c32a98SDimitry Andric }
124867c32a98SDimitry Andric
124901095a5dSDimitry Andric /// Convert an atomic cmpxchg of a non-integral type to an integer cmpxchg of
125001095a5dSDimitry Andric /// the equivalent bitwidth. We used to not support pointer cmpxchg in the
125101095a5dSDimitry Andric /// IR. As a migration step, we convert back to what use to be the standard
125201095a5dSDimitry Andric /// way to represent a pointer cmpxchg so that we can update backends one by
125301095a5dSDimitry Andric /// one.
1254145449b1SDimitry Andric AtomicCmpXchgInst *
convertCmpXchgToIntegerType(AtomicCmpXchgInst * CI)1255ac9a064cSDimitry Andric AtomicExpandImpl::convertCmpXchgToIntegerType(AtomicCmpXchgInst *CI) {
125601095a5dSDimitry Andric auto *M = CI->getModule();
125701095a5dSDimitry Andric Type *NewTy = getCorrespondingIntegerType(CI->getCompareOperand()->getType(),
125801095a5dSDimitry Andric M->getDataLayout());
125901095a5dSDimitry Andric
1260e3b55780SDimitry Andric ReplacementIRBuilder Builder(CI, *DL);
126101095a5dSDimitry Andric
126201095a5dSDimitry Andric Value *Addr = CI->getPointerOperand();
126301095a5dSDimitry Andric
126401095a5dSDimitry Andric Value *NewCmp = Builder.CreatePtrToInt(CI->getCompareOperand(), NewTy);
126501095a5dSDimitry Andric Value *NewNewVal = Builder.CreatePtrToInt(CI->getNewValOperand(), NewTy);
126601095a5dSDimitry Andric
1267344a3780SDimitry Andric auto *NewCI = Builder.CreateAtomicCmpXchg(
1268b1c73532SDimitry Andric Addr, NewCmp, NewNewVal, CI->getAlign(), CI->getSuccessOrdering(),
1269344a3780SDimitry Andric CI->getFailureOrdering(), CI->getSyncScopeID());
127001095a5dSDimitry Andric NewCI->setVolatile(CI->isVolatile());
127101095a5dSDimitry Andric NewCI->setWeak(CI->isWeak());
1272eb11fae6SDimitry Andric LLVM_DEBUG(dbgs() << "Replaced " << *CI << " with " << *NewCI << "\n");
127301095a5dSDimitry Andric
127401095a5dSDimitry Andric Value *OldVal = Builder.CreateExtractValue(NewCI, 0);
127501095a5dSDimitry Andric Value *Succ = Builder.CreateExtractValue(NewCI, 1);
127601095a5dSDimitry Andric
127701095a5dSDimitry Andric OldVal = Builder.CreateIntToPtr(OldVal, CI->getCompareOperand()->getType());
127801095a5dSDimitry Andric
1279e3b55780SDimitry Andric Value *Res = PoisonValue::get(CI->getType());
128001095a5dSDimitry Andric Res = Builder.CreateInsertValue(Res, OldVal, 0);
128101095a5dSDimitry Andric Res = Builder.CreateInsertValue(Res, Succ, 1);
128201095a5dSDimitry Andric
128301095a5dSDimitry Andric CI->replaceAllUsesWith(Res);
128401095a5dSDimitry Andric CI->eraseFromParent();
128501095a5dSDimitry Andric return NewCI;
128601095a5dSDimitry Andric }
128701095a5dSDimitry Andric
expandAtomicCmpXchg(AtomicCmpXchgInst * CI)1288ac9a064cSDimitry Andric bool AtomicExpandImpl::expandAtomicCmpXchg(AtomicCmpXchgInst *CI) {
128967c32a98SDimitry Andric AtomicOrdering SuccessOrder = CI->getSuccessOrdering();
129067c32a98SDimitry Andric AtomicOrdering FailureOrder = CI->getFailureOrdering();
129167c32a98SDimitry Andric Value *Addr = CI->getPointerOperand();
129267c32a98SDimitry Andric BasicBlock *BB = CI->getParent();
129367c32a98SDimitry Andric Function *F = BB->getParent();
129467c32a98SDimitry Andric LLVMContext &Ctx = F->getContext();
129501095a5dSDimitry Andric // If shouldInsertFencesForAtomic() returns true, then the target does not
129601095a5dSDimitry Andric // want to deal with memory orders, and emitLeading/TrailingFence should take
129701095a5dSDimitry Andric // care of everything. Otherwise, emitLeading/TrailingFence are no-op and we
129867c32a98SDimitry Andric // should preserve the ordering.
129901095a5dSDimitry Andric bool ShouldInsertFencesForAtomic = TLI->shouldInsertFencesForAtomic(CI);
1300344a3780SDimitry Andric AtomicOrdering MemOpOrder = ShouldInsertFencesForAtomic
1301344a3780SDimitry Andric ? AtomicOrdering::Monotonic
1302344a3780SDimitry Andric : CI->getMergedOrdering();
130301095a5dSDimitry Andric
130401095a5dSDimitry Andric // In implementations which use a barrier to achieve release semantics, we can
130501095a5dSDimitry Andric // delay emitting this barrier until we know a store is actually going to be
130601095a5dSDimitry Andric // attempted. The cost of this delay is that we need 2 copies of the block
130701095a5dSDimitry Andric // emitting the load-linked, affecting code size.
130801095a5dSDimitry Andric //
130901095a5dSDimitry Andric // Ideally, this logic would be unconditional except for the minsize check
131001095a5dSDimitry Andric // since in other cases the extra blocks naturally collapse down to the
131101095a5dSDimitry Andric // minimal loop. Unfortunately, this puts too much stress on later
131201095a5dSDimitry Andric // optimisations so we avoid emitting the extra logic in those cases too.
131301095a5dSDimitry Andric bool HasReleasedLoadBB = !CI->isWeak() && ShouldInsertFencesForAtomic &&
131401095a5dSDimitry Andric SuccessOrder != AtomicOrdering::Monotonic &&
131501095a5dSDimitry Andric SuccessOrder != AtomicOrdering::Acquire &&
1316e6d15924SDimitry Andric !F->hasMinSize();
131701095a5dSDimitry Andric
131801095a5dSDimitry Andric // There's no overhead for sinking the release barrier in a weak cmpxchg, so
131901095a5dSDimitry Andric // do it even on minsize.
1320e6d15924SDimitry Andric bool UseUnconditionalReleaseBarrier = F->hasMinSize() && !CI->isWeak();
132167c32a98SDimitry Andric
132267c32a98SDimitry Andric // Given: cmpxchg some_op iN* %addr, iN %desired, iN %new success_ord fail_ord
132367c32a98SDimitry Andric //
132467c32a98SDimitry Andric // The full expansion we produce is:
132567c32a98SDimitry Andric // [...]
1326cfca06d7SDimitry Andric // %aligned.addr = ...
132767c32a98SDimitry Andric // cmpxchg.start:
1328cfca06d7SDimitry Andric // %unreleasedload = @load.linked(%aligned.addr)
1329cfca06d7SDimitry Andric // %unreleasedload.extract = extract value from %unreleasedload
1330cfca06d7SDimitry Andric // %should_store = icmp eq %unreleasedload.extract, %desired
1331cfca06d7SDimitry Andric // br i1 %should_store, label %cmpxchg.releasingstore,
1332dd58ef01SDimitry Andric // label %cmpxchg.nostore
133301095a5dSDimitry Andric // cmpxchg.releasingstore:
133401095a5dSDimitry Andric // fence?
133501095a5dSDimitry Andric // br label cmpxchg.trystore
133667c32a98SDimitry Andric // cmpxchg.trystore:
1337cfca06d7SDimitry Andric // %loaded.trystore = phi [%unreleasedload, %cmpxchg.releasingstore],
133801095a5dSDimitry Andric // [%releasedload, %cmpxchg.releasedload]
1339cfca06d7SDimitry Andric // %updated.new = insert %new into %loaded.trystore
1340cfca06d7SDimitry Andric // %stored = @store_conditional(%updated.new, %aligned.addr)
134167c32a98SDimitry Andric // %success = icmp eq i32 %stored, 0
134201095a5dSDimitry Andric // br i1 %success, label %cmpxchg.success,
134301095a5dSDimitry Andric // label %cmpxchg.releasedload/%cmpxchg.failure
134401095a5dSDimitry Andric // cmpxchg.releasedload:
1345cfca06d7SDimitry Andric // %releasedload = @load.linked(%aligned.addr)
1346cfca06d7SDimitry Andric // %releasedload.extract = extract value from %releasedload
1347cfca06d7SDimitry Andric // %should_store = icmp eq %releasedload.extract, %desired
134801095a5dSDimitry Andric // br i1 %should_store, label %cmpxchg.trystore,
134901095a5dSDimitry Andric // label %cmpxchg.failure
135067c32a98SDimitry Andric // cmpxchg.success:
135167c32a98SDimitry Andric // fence?
135267c32a98SDimitry Andric // br label %cmpxchg.end
1353dd58ef01SDimitry Andric // cmpxchg.nostore:
135401095a5dSDimitry Andric // %loaded.nostore = phi [%unreleasedload, %cmpxchg.start],
135501095a5dSDimitry Andric // [%releasedload,
135601095a5dSDimitry Andric // %cmpxchg.releasedload/%cmpxchg.trystore]
1357dd58ef01SDimitry Andric // @load_linked_fail_balance()?
1358dd58ef01SDimitry Andric // br label %cmpxchg.failure
135967c32a98SDimitry Andric // cmpxchg.failure:
136067c32a98SDimitry Andric // fence?
136167c32a98SDimitry Andric // br label %cmpxchg.end
136267c32a98SDimitry Andric // cmpxchg.end:
1363cfca06d7SDimitry Andric // %loaded.exit = phi [%loaded.nostore, %cmpxchg.failure],
136401095a5dSDimitry Andric // [%loaded.trystore, %cmpxchg.trystore]
136567c32a98SDimitry Andric // %success = phi i1 [true, %cmpxchg.success], [false, %cmpxchg.failure]
1366cfca06d7SDimitry Andric // %loaded = extract value from %loaded.exit
136767c32a98SDimitry Andric // %restmp = insertvalue { iN, i1 } undef, iN %loaded, 0
136867c32a98SDimitry Andric // %res = insertvalue { iN, i1 } %restmp, i1 %success, 1
136967c32a98SDimitry Andric // [...]
1370dd58ef01SDimitry Andric BasicBlock *ExitBB = BB->splitBasicBlock(CI->getIterator(), "cmpxchg.end");
137167c32a98SDimitry Andric auto FailureBB = BasicBlock::Create(Ctx, "cmpxchg.failure", F, ExitBB);
1372dd58ef01SDimitry Andric auto NoStoreBB = BasicBlock::Create(Ctx, "cmpxchg.nostore", F, FailureBB);
1373dd58ef01SDimitry Andric auto SuccessBB = BasicBlock::Create(Ctx, "cmpxchg.success", F, NoStoreBB);
137401095a5dSDimitry Andric auto ReleasedLoadBB =
137501095a5dSDimitry Andric BasicBlock::Create(Ctx, "cmpxchg.releasedload", F, SuccessBB);
137601095a5dSDimitry Andric auto TryStoreBB =
137701095a5dSDimitry Andric BasicBlock::Create(Ctx, "cmpxchg.trystore", F, ReleasedLoadBB);
137801095a5dSDimitry Andric auto ReleasingStoreBB =
137901095a5dSDimitry Andric BasicBlock::Create(Ctx, "cmpxchg.fencedstore", F, TryStoreBB);
138001095a5dSDimitry Andric auto StartBB = BasicBlock::Create(Ctx, "cmpxchg.start", F, ReleasingStoreBB);
138167c32a98SDimitry Andric
1382e3b55780SDimitry Andric ReplacementIRBuilder Builder(CI, *DL);
138367c32a98SDimitry Andric
138467c32a98SDimitry Andric // The split call above "helpfully" added a branch at the end of BB (to the
138567c32a98SDimitry Andric // wrong place), but we might want a fence too. It's easiest to just remove
138667c32a98SDimitry Andric // the branch entirely.
138767c32a98SDimitry Andric std::prev(BB->end())->eraseFromParent();
138867c32a98SDimitry Andric Builder.SetInsertPoint(BB);
138901095a5dSDimitry Andric if (ShouldInsertFencesForAtomic && UseUnconditionalReleaseBarrier)
13906b3f41edSDimitry Andric TLI->emitLeadingFence(Builder, CI, SuccessOrder);
1391cfca06d7SDimitry Andric
1392cfca06d7SDimitry Andric PartwordMaskValues PMV =
1393cfca06d7SDimitry Andric createMaskInstrs(Builder, CI, CI->getCompareOperand()->getType(), Addr,
1394344a3780SDimitry Andric CI->getAlign(), TLI->getMinCmpXchgSizeInBits() / 8);
139501095a5dSDimitry Andric Builder.CreateBr(StartBB);
139667c32a98SDimitry Andric
139767c32a98SDimitry Andric // Start the main loop block now that we've taken care of the preliminaries.
139801095a5dSDimitry Andric Builder.SetInsertPoint(StartBB);
1399cfca06d7SDimitry Andric Value *UnreleasedLoad =
1400344a3780SDimitry Andric TLI->emitLoadLinked(Builder, PMV.WordType, PMV.AlignedAddr, MemOpOrder);
1401cfca06d7SDimitry Andric Value *UnreleasedLoadExtract =
1402cfca06d7SDimitry Andric extractMaskedValue(Builder, UnreleasedLoad, PMV);
140301095a5dSDimitry Andric Value *ShouldStore = Builder.CreateICmpEQ(
1404cfca06d7SDimitry Andric UnreleasedLoadExtract, CI->getCompareOperand(), "should_store");
140567c32a98SDimitry Andric
14063a0822f0SDimitry Andric // If the cmpxchg doesn't actually need any ordering when it fails, we can
140767c32a98SDimitry Andric // jump straight past that fence instruction (if it exists).
140801095a5dSDimitry Andric Builder.CreateCondBr(ShouldStore, ReleasingStoreBB, NoStoreBB);
140901095a5dSDimitry Andric
141001095a5dSDimitry Andric Builder.SetInsertPoint(ReleasingStoreBB);
141101095a5dSDimitry Andric if (ShouldInsertFencesForAtomic && !UseUnconditionalReleaseBarrier)
14126b3f41edSDimitry Andric TLI->emitLeadingFence(Builder, CI, SuccessOrder);
141301095a5dSDimitry Andric Builder.CreateBr(TryStoreBB);
141467c32a98SDimitry Andric
141567c32a98SDimitry Andric Builder.SetInsertPoint(TryStoreBB);
1416cfca06d7SDimitry Andric PHINode *LoadedTryStore =
1417cfca06d7SDimitry Andric Builder.CreatePHI(PMV.WordType, 2, "loaded.trystore");
1418cfca06d7SDimitry Andric LoadedTryStore->addIncoming(UnreleasedLoad, ReleasingStoreBB);
1419cfca06d7SDimitry Andric Value *NewValueInsert =
1420cfca06d7SDimitry Andric insertMaskedValue(Builder, LoadedTryStore, CI->getNewValOperand(), PMV);
1421145449b1SDimitry Andric Value *StoreSuccess = TLI->emitStoreConditional(Builder, NewValueInsert,
1422145449b1SDimitry Andric PMV.AlignedAddr, MemOpOrder);
142367c32a98SDimitry Andric StoreSuccess = Builder.CreateICmpEQ(
142467c32a98SDimitry Andric StoreSuccess, ConstantInt::get(Type::getInt32Ty(Ctx), 0), "success");
142501095a5dSDimitry Andric BasicBlock *RetryBB = HasReleasedLoadBB ? ReleasedLoadBB : StartBB;
142667c32a98SDimitry Andric Builder.CreateCondBr(StoreSuccess, SuccessBB,
142701095a5dSDimitry Andric CI->isWeak() ? FailureBB : RetryBB);
142867c32a98SDimitry Andric
142901095a5dSDimitry Andric Builder.SetInsertPoint(ReleasedLoadBB);
143001095a5dSDimitry Andric Value *SecondLoad;
143101095a5dSDimitry Andric if (HasReleasedLoadBB) {
1432344a3780SDimitry Andric SecondLoad =
1433344a3780SDimitry Andric TLI->emitLoadLinked(Builder, PMV.WordType, PMV.AlignedAddr, MemOpOrder);
1434cfca06d7SDimitry Andric Value *SecondLoadExtract = extractMaskedValue(Builder, SecondLoad, PMV);
1435cfca06d7SDimitry Andric ShouldStore = Builder.CreateICmpEQ(SecondLoadExtract,
1436cfca06d7SDimitry Andric CI->getCompareOperand(), "should_store");
143701095a5dSDimitry Andric
143801095a5dSDimitry Andric // If the cmpxchg doesn't actually need any ordering when it fails, we can
143901095a5dSDimitry Andric // jump straight past that fence instruction (if it exists).
144001095a5dSDimitry Andric Builder.CreateCondBr(ShouldStore, TryStoreBB, NoStoreBB);
1441cfca06d7SDimitry Andric // Update PHI node in TryStoreBB.
1442cfca06d7SDimitry Andric LoadedTryStore->addIncoming(SecondLoad, ReleasedLoadBB);
144301095a5dSDimitry Andric } else
144401095a5dSDimitry Andric Builder.CreateUnreachable();
144501095a5dSDimitry Andric
144601095a5dSDimitry Andric // Make sure later instructions don't get reordered with a fence if
144701095a5dSDimitry Andric // necessary.
144867c32a98SDimitry Andric Builder.SetInsertPoint(SuccessBB);
1449e3b55780SDimitry Andric if (ShouldInsertFencesForAtomic ||
1450e3b55780SDimitry Andric TLI->shouldInsertTrailingFenceForAtomicStore(CI))
14516b3f41edSDimitry Andric TLI->emitTrailingFence(Builder, CI, SuccessOrder);
145267c32a98SDimitry Andric Builder.CreateBr(ExitBB);
145367c32a98SDimitry Andric
1454dd58ef01SDimitry Andric Builder.SetInsertPoint(NoStoreBB);
1455cfca06d7SDimitry Andric PHINode *LoadedNoStore =
1456cfca06d7SDimitry Andric Builder.CreatePHI(UnreleasedLoad->getType(), 2, "loaded.nostore");
1457cfca06d7SDimitry Andric LoadedNoStore->addIncoming(UnreleasedLoad, StartBB);
1458cfca06d7SDimitry Andric if (HasReleasedLoadBB)
1459cfca06d7SDimitry Andric LoadedNoStore->addIncoming(SecondLoad, ReleasedLoadBB);
1460cfca06d7SDimitry Andric
1461dd58ef01SDimitry Andric // In the failing case, where we don't execute the store-conditional, the
1462dd58ef01SDimitry Andric // target might want to balance out the load-linked with a dedicated
1463dd58ef01SDimitry Andric // instruction (e.g., on ARM, clearing the exclusive monitor).
1464dd58ef01SDimitry Andric TLI->emitAtomicCmpXchgNoStoreLLBalance(Builder);
1465dd58ef01SDimitry Andric Builder.CreateBr(FailureBB);
1466dd58ef01SDimitry Andric
146767c32a98SDimitry Andric Builder.SetInsertPoint(FailureBB);
1468cfca06d7SDimitry Andric PHINode *LoadedFailure =
1469cfca06d7SDimitry Andric Builder.CreatePHI(UnreleasedLoad->getType(), 2, "loaded.failure");
1470cfca06d7SDimitry Andric LoadedFailure->addIncoming(LoadedNoStore, NoStoreBB);
1471cfca06d7SDimitry Andric if (CI->isWeak())
1472cfca06d7SDimitry Andric LoadedFailure->addIncoming(LoadedTryStore, TryStoreBB);
147301095a5dSDimitry Andric if (ShouldInsertFencesForAtomic)
14746b3f41edSDimitry Andric TLI->emitTrailingFence(Builder, CI, FailureOrder);
147567c32a98SDimitry Andric Builder.CreateBr(ExitBB);
147667c32a98SDimitry Andric
147767c32a98SDimitry Andric // Finally, we have control-flow based knowledge of whether the cmpxchg
147867c32a98SDimitry Andric // succeeded or not. We expose this to later passes by converting any
147901095a5dSDimitry Andric // subsequent "icmp eq/ne %loaded, %oldval" into a use of an appropriate
148001095a5dSDimitry Andric // PHI.
148167c32a98SDimitry Andric Builder.SetInsertPoint(ExitBB, ExitBB->begin());
1482cfca06d7SDimitry Andric PHINode *LoadedExit =
1483cfca06d7SDimitry Andric Builder.CreatePHI(UnreleasedLoad->getType(), 2, "loaded.exit");
1484cfca06d7SDimitry Andric LoadedExit->addIncoming(LoadedTryStore, SuccessBB);
1485cfca06d7SDimitry Andric LoadedExit->addIncoming(LoadedFailure, FailureBB);
1486cfca06d7SDimitry Andric PHINode *Success = Builder.CreatePHI(Type::getInt1Ty(Ctx), 2, "success");
148767c32a98SDimitry Andric Success->addIncoming(ConstantInt::getTrue(Ctx), SuccessBB);
148867c32a98SDimitry Andric Success->addIncoming(ConstantInt::getFalse(Ctx), FailureBB);
148967c32a98SDimitry Andric
1490cfca06d7SDimitry Andric // This is the "exit value" from the cmpxchg expansion. It may be of
1491cfca06d7SDimitry Andric // a type wider than the one in the cmpxchg instruction.
1492cfca06d7SDimitry Andric Value *LoadedFull = LoadedExit;
149301095a5dSDimitry Andric
1494cfca06d7SDimitry Andric Builder.SetInsertPoint(ExitBB, std::next(Success->getIterator()));
1495cfca06d7SDimitry Andric Value *Loaded = extractMaskedValue(Builder, LoadedFull, PMV);
149601095a5dSDimitry Andric
149767c32a98SDimitry Andric // Look for any users of the cmpxchg that are just comparing the loaded value
149867c32a98SDimitry Andric // against the desired one, and replace them with the CFG-derived version.
149967c32a98SDimitry Andric SmallVector<ExtractValueInst *, 2> PrunedInsts;
15004b4fe385SDimitry Andric for (auto *User : CI->users()) {
150167c32a98SDimitry Andric ExtractValueInst *EV = dyn_cast<ExtractValueInst>(User);
150267c32a98SDimitry Andric if (!EV)
150367c32a98SDimitry Andric continue;
150467c32a98SDimitry Andric
150567c32a98SDimitry Andric assert(EV->getNumIndices() == 1 && EV->getIndices()[0] <= 1 &&
150667c32a98SDimitry Andric "weird extraction from { iN, i1 }");
150767c32a98SDimitry Andric
150867c32a98SDimitry Andric if (EV->getIndices()[0] == 0)
150967c32a98SDimitry Andric EV->replaceAllUsesWith(Loaded);
151067c32a98SDimitry Andric else
151167c32a98SDimitry Andric EV->replaceAllUsesWith(Success);
151267c32a98SDimitry Andric
151367c32a98SDimitry Andric PrunedInsts.push_back(EV);
151467c32a98SDimitry Andric }
151567c32a98SDimitry Andric
151667c32a98SDimitry Andric // We can remove the instructions now we're no longer iterating through them.
15174b4fe385SDimitry Andric for (auto *EV : PrunedInsts)
151867c32a98SDimitry Andric EV->eraseFromParent();
151967c32a98SDimitry Andric
152067c32a98SDimitry Andric if (!CI->use_empty()) {
152167c32a98SDimitry Andric // Some use of the full struct return that we don't understand has happened,
152267c32a98SDimitry Andric // so we've got to reconstruct it properly.
152367c32a98SDimitry Andric Value *Res;
1524e3b55780SDimitry Andric Res = Builder.CreateInsertValue(PoisonValue::get(CI->getType()), Loaded, 0);
152567c32a98SDimitry Andric Res = Builder.CreateInsertValue(Res, Success, 1);
152667c32a98SDimitry Andric
152767c32a98SDimitry Andric CI->replaceAllUsesWith(Res);
152867c32a98SDimitry Andric }
152967c32a98SDimitry Andric
153067c32a98SDimitry Andric CI->eraseFromParent();
153167c32a98SDimitry Andric return true;
153267c32a98SDimitry Andric }
153367c32a98SDimitry Andric
isIdempotentRMW(AtomicRMWInst * RMWI)1534ac9a064cSDimitry Andric bool AtomicExpandImpl::isIdempotentRMW(AtomicRMWInst *RMWI) {
153567c32a98SDimitry Andric auto C = dyn_cast<ConstantInt>(RMWI->getValOperand());
153667c32a98SDimitry Andric if (!C)
153767c32a98SDimitry Andric return false;
153867c32a98SDimitry Andric
153967c32a98SDimitry Andric AtomicRMWInst::BinOp Op = RMWI->getOperation();
154067c32a98SDimitry Andric switch (Op) {
154167c32a98SDimitry Andric case AtomicRMWInst::Add:
154267c32a98SDimitry Andric case AtomicRMWInst::Sub:
154367c32a98SDimitry Andric case AtomicRMWInst::Or:
154467c32a98SDimitry Andric case AtomicRMWInst::Xor:
154567c32a98SDimitry Andric return C->isZero();
154667c32a98SDimitry Andric case AtomicRMWInst::And:
154767c32a98SDimitry Andric return C->isMinusOne();
154867c32a98SDimitry Andric // FIXME: we could also treat Min/Max/UMin/UMax by the INT_MIN/INT_MAX/...
154967c32a98SDimitry Andric default:
155067c32a98SDimitry Andric return false;
155167c32a98SDimitry Andric }
155267c32a98SDimitry Andric }
155367c32a98SDimitry Andric
simplifyIdempotentRMW(AtomicRMWInst * RMWI)1554ac9a064cSDimitry Andric bool AtomicExpandImpl::simplifyIdempotentRMW(AtomicRMWInst *RMWI) {
155567c32a98SDimitry Andric if (auto ResultingLoad = TLI->lowerIdempotentRMWIntoFencedLoad(RMWI)) {
1556dd58ef01SDimitry Andric tryExpandAtomicLoad(ResultingLoad);
155767c32a98SDimitry Andric return true;
155867c32a98SDimitry Andric }
155967c32a98SDimitry Andric return false;
156067c32a98SDimitry Andric }
1561dd58ef01SDimitry Andric
insertRMWCmpXchgLoop(IRBuilderBase & Builder,Type * ResultTy,Value * Addr,Align AddrAlign,AtomicOrdering MemOpOrder,SyncScope::ID SSID,function_ref<Value * (IRBuilderBase &,Value *)> PerformOp,CreateCmpXchgInstFun CreateCmpXchg)1562ac9a064cSDimitry Andric Value *AtomicExpandImpl::insertRMWCmpXchgLoop(
1563e3b55780SDimitry Andric IRBuilderBase &Builder, Type *ResultTy, Value *Addr, Align AddrAlign,
1564344a3780SDimitry Andric AtomicOrdering MemOpOrder, SyncScope::ID SSID,
1565e3b55780SDimitry Andric function_ref<Value *(IRBuilderBase &, Value *)> PerformOp,
1566dd58ef01SDimitry Andric CreateCmpXchgInstFun CreateCmpXchg) {
156701095a5dSDimitry Andric LLVMContext &Ctx = Builder.getContext();
156801095a5dSDimitry Andric BasicBlock *BB = Builder.GetInsertBlock();
1569dd58ef01SDimitry Andric Function *F = BB->getParent();
1570dd58ef01SDimitry Andric
1571dd58ef01SDimitry Andric // Given: atomicrmw some_op iN* %addr, iN %incr ordering
1572dd58ef01SDimitry Andric //
1573dd58ef01SDimitry Andric // The standard expansion we produce is:
1574dd58ef01SDimitry Andric // [...]
1575dd58ef01SDimitry Andric // %init_loaded = load atomic iN* %addr
1576dd58ef01SDimitry Andric // br label %loop
1577dd58ef01SDimitry Andric // loop:
1578dd58ef01SDimitry Andric // %loaded = phi iN [ %init_loaded, %entry ], [ %new_loaded, %loop ]
1579dd58ef01SDimitry Andric // %new = some_op iN %loaded, %incr
1580dd58ef01SDimitry Andric // %pair = cmpxchg iN* %addr, iN %loaded, iN %new
1581dd58ef01SDimitry Andric // %new_loaded = extractvalue { iN, i1 } %pair, 0
1582dd58ef01SDimitry Andric // %success = extractvalue { iN, i1 } %pair, 1
1583dd58ef01SDimitry Andric // br i1 %success, label %atomicrmw.end, label %loop
1584dd58ef01SDimitry Andric // atomicrmw.end:
1585dd58ef01SDimitry Andric // [...]
158601095a5dSDimitry Andric BasicBlock *ExitBB =
158701095a5dSDimitry Andric BB->splitBasicBlock(Builder.GetInsertPoint(), "atomicrmw.end");
1588dd58ef01SDimitry Andric BasicBlock *LoopBB = BasicBlock::Create(Ctx, "atomicrmw.start", F, ExitBB);
1589dd58ef01SDimitry Andric
1590dd58ef01SDimitry Andric // The split call above "helpfully" added a branch at the end of BB (to the
1591dd58ef01SDimitry Andric // wrong place), but we want a load. It's easiest to just remove
1592dd58ef01SDimitry Andric // the branch entirely.
1593dd58ef01SDimitry Andric std::prev(BB->end())->eraseFromParent();
1594dd58ef01SDimitry Andric Builder.SetInsertPoint(BB);
1595344a3780SDimitry Andric LoadInst *InitLoaded = Builder.CreateAlignedLoad(ResultTy, Addr, AddrAlign);
1596dd58ef01SDimitry Andric Builder.CreateBr(LoopBB);
1597dd58ef01SDimitry Andric
1598dd58ef01SDimitry Andric // Start the main loop block now that we've taken care of the preliminaries.
1599dd58ef01SDimitry Andric Builder.SetInsertPoint(LoopBB);
160001095a5dSDimitry Andric PHINode *Loaded = Builder.CreatePHI(ResultTy, 2, "loaded");
1601dd58ef01SDimitry Andric Loaded->addIncoming(InitLoaded, BB);
1602dd58ef01SDimitry Andric
160301095a5dSDimitry Andric Value *NewVal = PerformOp(Builder, Loaded);
1604dd58ef01SDimitry Andric
1605dd58ef01SDimitry Andric Value *NewLoaded = nullptr;
1606dd58ef01SDimitry Andric Value *Success = nullptr;
1607dd58ef01SDimitry Andric
1608344a3780SDimitry Andric CreateCmpXchg(Builder, Addr, Loaded, NewVal, AddrAlign,
160901095a5dSDimitry Andric MemOpOrder == AtomicOrdering::Unordered
161001095a5dSDimitry Andric ? AtomicOrdering::Monotonic
161101095a5dSDimitry Andric : MemOpOrder,
1612344a3780SDimitry Andric SSID, Success, NewLoaded);
1613dd58ef01SDimitry Andric assert(Success && NewLoaded);
1614dd58ef01SDimitry Andric
1615dd58ef01SDimitry Andric Loaded->addIncoming(NewLoaded, LoopBB);
1616dd58ef01SDimitry Andric
1617dd58ef01SDimitry Andric Builder.CreateCondBr(Success, ExitBB, LoopBB);
1618dd58ef01SDimitry Andric
1619dd58ef01SDimitry Andric Builder.SetInsertPoint(ExitBB, ExitBB->begin());
162001095a5dSDimitry Andric return NewLoaded;
162101095a5dSDimitry Andric }
1622dd58ef01SDimitry Andric
tryExpandAtomicCmpXchg(AtomicCmpXchgInst * CI)1623ac9a064cSDimitry Andric bool AtomicExpandImpl::tryExpandAtomicCmpXchg(AtomicCmpXchgInst *CI) {
1624d8e91e46SDimitry Andric unsigned MinCASSize = TLI->getMinCmpXchgSizeInBits() / 8;
1625d8e91e46SDimitry Andric unsigned ValueSize = getAtomicOpSize(CI);
1626d8e91e46SDimitry Andric
1627d8e91e46SDimitry Andric switch (TLI->shouldExpandAtomicCmpXchgInIR(CI)) {
1628d8e91e46SDimitry Andric default:
1629d8e91e46SDimitry Andric llvm_unreachable("Unhandled case in tryExpandAtomicCmpXchg");
1630d8e91e46SDimitry Andric case TargetLoweringBase::AtomicExpansionKind::None:
1631d8e91e46SDimitry Andric if (ValueSize < MinCASSize)
1632cfca06d7SDimitry Andric return expandPartwordCmpXchg(CI);
1633d8e91e46SDimitry Andric return false;
1634d8e91e46SDimitry Andric case TargetLoweringBase::AtomicExpansionKind::LLSC: {
1635d8e91e46SDimitry Andric return expandAtomicCmpXchg(CI);
1636d8e91e46SDimitry Andric }
1637d8e91e46SDimitry Andric case TargetLoweringBase::AtomicExpansionKind::MaskedIntrinsic:
1638d8e91e46SDimitry Andric expandAtomicCmpXchgToMaskedIntrinsic(CI);
1639d8e91e46SDimitry Andric return true;
1640145449b1SDimitry Andric case TargetLoweringBase::AtomicExpansionKind::NotAtomic:
1641145449b1SDimitry Andric return lowerAtomicCmpXchgInst(CI);
1642d8e91e46SDimitry Andric }
1643d8e91e46SDimitry Andric }
1644d8e91e46SDimitry Andric
164501095a5dSDimitry Andric // Note: This function is exposed externally by AtomicExpandUtils.h
expandAtomicRMWToCmpXchg(AtomicRMWInst * AI,CreateCmpXchgInstFun CreateCmpXchg)164601095a5dSDimitry Andric bool llvm::expandAtomicRMWToCmpXchg(AtomicRMWInst *AI,
164701095a5dSDimitry Andric CreateCmpXchgInstFun CreateCmpXchg) {
1648ac9a064cSDimitry Andric ReplacementIRBuilder Builder(AI, AI->getDataLayout());
16497fa27ce4SDimitry Andric Builder.setIsFPConstrained(
16507fa27ce4SDimitry Andric AI->getFunction()->hasFnAttribute(Attribute::StrictFP));
16517fa27ce4SDimitry Andric
16527fa27ce4SDimitry Andric // FIXME: If FP exceptions are observable, we should force them off for the
16537fa27ce4SDimitry Andric // loop for the FP atomics.
1654ac9a064cSDimitry Andric Value *Loaded = AtomicExpandImpl::insertRMWCmpXchgLoop(
1655344a3780SDimitry Andric Builder, AI->getType(), AI->getPointerOperand(), AI->getAlign(),
1656344a3780SDimitry Andric AI->getOrdering(), AI->getSyncScopeID(),
1657e3b55780SDimitry Andric [&](IRBuilderBase &Builder, Value *Loaded) {
1658145449b1SDimitry Andric return buildAtomicRMWValue(AI->getOperation(), Builder, Loaded,
165901095a5dSDimitry Andric AI->getValOperand());
166001095a5dSDimitry Andric },
166101095a5dSDimitry Andric CreateCmpXchg);
166201095a5dSDimitry Andric
166301095a5dSDimitry Andric AI->replaceAllUsesWith(Loaded);
1664dd58ef01SDimitry Andric AI->eraseFromParent();
166501095a5dSDimitry Andric return true;
166601095a5dSDimitry Andric }
1667dd58ef01SDimitry Andric
166801095a5dSDimitry Andric // In order to use one of the sized library calls such as
166901095a5dSDimitry Andric // __atomic_fetch_add_4, the alignment must be sufficient, the size
167001095a5dSDimitry Andric // must be one of the potentially-specialized sizes, and the value
167101095a5dSDimitry Andric // type must actually exist in C on the target (otherwise, the
167201095a5dSDimitry Andric // function wouldn't actually be defined.)
canUseSizedAtomicCall(unsigned Size,Align Alignment,const DataLayout & DL)1673cfca06d7SDimitry Andric static bool canUseSizedAtomicCall(unsigned Size, Align Alignment,
167401095a5dSDimitry Andric const DataLayout &DL) {
167501095a5dSDimitry Andric // TODO: "LargestSize" is an approximation for "largest type that
167601095a5dSDimitry Andric // you can express in C". It seems to be the case that int128 is
167701095a5dSDimitry Andric // supported on all 64-bit platforms, otherwise only up to 64-bit
167801095a5dSDimitry Andric // integers are supported. If we get this wrong, then we'll try to
167901095a5dSDimitry Andric // call a sized libcall that doesn't actually exist. There should
168001095a5dSDimitry Andric // really be some more reliable way in LLVM of determining integer
168101095a5dSDimitry Andric // sizes which are valid in the target's C ABI...
168201095a5dSDimitry Andric unsigned LargestSize = DL.getLargestLegalIntTypeSizeInBits() >= 64 ? 16 : 8;
1683cfca06d7SDimitry Andric return Alignment >= Size &&
168401095a5dSDimitry Andric (Size == 1 || Size == 2 || Size == 4 || Size == 8 || Size == 16) &&
168501095a5dSDimitry Andric Size <= LargestSize;
168601095a5dSDimitry Andric }
168701095a5dSDimitry Andric
expandAtomicLoadToLibcall(LoadInst * I)1688ac9a064cSDimitry Andric void AtomicExpandImpl::expandAtomicLoadToLibcall(LoadInst *I) {
168901095a5dSDimitry Andric static const RTLIB::Libcall Libcalls[6] = {
169001095a5dSDimitry Andric RTLIB::ATOMIC_LOAD, RTLIB::ATOMIC_LOAD_1, RTLIB::ATOMIC_LOAD_2,
169101095a5dSDimitry Andric RTLIB::ATOMIC_LOAD_4, RTLIB::ATOMIC_LOAD_8, RTLIB::ATOMIC_LOAD_16};
169201095a5dSDimitry Andric unsigned Size = getAtomicOpSize(I);
169301095a5dSDimitry Andric
169401095a5dSDimitry Andric bool expanded = expandAtomicOpToLibcall(
1695cfca06d7SDimitry Andric I, Size, I->getAlign(), I->getPointerOperand(), nullptr, nullptr,
169601095a5dSDimitry Andric I->getOrdering(), AtomicOrdering::NotAtomic, Libcalls);
1697b60736ecSDimitry Andric if (!expanded)
1698b60736ecSDimitry Andric report_fatal_error("expandAtomicOpToLibcall shouldn't fail for Load");
169901095a5dSDimitry Andric }
170001095a5dSDimitry Andric
expandAtomicStoreToLibcall(StoreInst * I)1701ac9a064cSDimitry Andric void AtomicExpandImpl::expandAtomicStoreToLibcall(StoreInst *I) {
170201095a5dSDimitry Andric static const RTLIB::Libcall Libcalls[6] = {
170301095a5dSDimitry Andric RTLIB::ATOMIC_STORE, RTLIB::ATOMIC_STORE_1, RTLIB::ATOMIC_STORE_2,
170401095a5dSDimitry Andric RTLIB::ATOMIC_STORE_4, RTLIB::ATOMIC_STORE_8, RTLIB::ATOMIC_STORE_16};
170501095a5dSDimitry Andric unsigned Size = getAtomicOpSize(I);
170601095a5dSDimitry Andric
170701095a5dSDimitry Andric bool expanded = expandAtomicOpToLibcall(
1708cfca06d7SDimitry Andric I, Size, I->getAlign(), I->getPointerOperand(), I->getValueOperand(),
1709cfca06d7SDimitry Andric nullptr, I->getOrdering(), AtomicOrdering::NotAtomic, Libcalls);
1710b60736ecSDimitry Andric if (!expanded)
1711b60736ecSDimitry Andric report_fatal_error("expandAtomicOpToLibcall shouldn't fail for Store");
171201095a5dSDimitry Andric }
171301095a5dSDimitry Andric
expandAtomicCASToLibcall(AtomicCmpXchgInst * I)1714ac9a064cSDimitry Andric void AtomicExpandImpl::expandAtomicCASToLibcall(AtomicCmpXchgInst *I) {
171501095a5dSDimitry Andric static const RTLIB::Libcall Libcalls[6] = {
171601095a5dSDimitry Andric RTLIB::ATOMIC_COMPARE_EXCHANGE, RTLIB::ATOMIC_COMPARE_EXCHANGE_1,
171701095a5dSDimitry Andric RTLIB::ATOMIC_COMPARE_EXCHANGE_2, RTLIB::ATOMIC_COMPARE_EXCHANGE_4,
171801095a5dSDimitry Andric RTLIB::ATOMIC_COMPARE_EXCHANGE_8, RTLIB::ATOMIC_COMPARE_EXCHANGE_16};
171901095a5dSDimitry Andric unsigned Size = getAtomicOpSize(I);
172001095a5dSDimitry Andric
172101095a5dSDimitry Andric bool expanded = expandAtomicOpToLibcall(
1722cfca06d7SDimitry Andric I, Size, I->getAlign(), I->getPointerOperand(), I->getNewValOperand(),
172301095a5dSDimitry Andric I->getCompareOperand(), I->getSuccessOrdering(), I->getFailureOrdering(),
172401095a5dSDimitry Andric Libcalls);
1725b60736ecSDimitry Andric if (!expanded)
1726b60736ecSDimitry Andric report_fatal_error("expandAtomicOpToLibcall shouldn't fail for CAS");
172701095a5dSDimitry Andric }
172801095a5dSDimitry Andric
GetRMWLibcall(AtomicRMWInst::BinOp Op)172901095a5dSDimitry Andric static ArrayRef<RTLIB::Libcall> GetRMWLibcall(AtomicRMWInst::BinOp Op) {
173001095a5dSDimitry Andric static const RTLIB::Libcall LibcallsXchg[6] = {
173101095a5dSDimitry Andric RTLIB::ATOMIC_EXCHANGE, RTLIB::ATOMIC_EXCHANGE_1,
173201095a5dSDimitry Andric RTLIB::ATOMIC_EXCHANGE_2, RTLIB::ATOMIC_EXCHANGE_4,
173301095a5dSDimitry Andric RTLIB::ATOMIC_EXCHANGE_8, RTLIB::ATOMIC_EXCHANGE_16};
173401095a5dSDimitry Andric static const RTLIB::Libcall LibcallsAdd[6] = {
173501095a5dSDimitry Andric RTLIB::UNKNOWN_LIBCALL, RTLIB::ATOMIC_FETCH_ADD_1,
173601095a5dSDimitry Andric RTLIB::ATOMIC_FETCH_ADD_2, RTLIB::ATOMIC_FETCH_ADD_4,
173701095a5dSDimitry Andric RTLIB::ATOMIC_FETCH_ADD_8, RTLIB::ATOMIC_FETCH_ADD_16};
173801095a5dSDimitry Andric static const RTLIB::Libcall LibcallsSub[6] = {
173901095a5dSDimitry Andric RTLIB::UNKNOWN_LIBCALL, RTLIB::ATOMIC_FETCH_SUB_1,
174001095a5dSDimitry Andric RTLIB::ATOMIC_FETCH_SUB_2, RTLIB::ATOMIC_FETCH_SUB_4,
174101095a5dSDimitry Andric RTLIB::ATOMIC_FETCH_SUB_8, RTLIB::ATOMIC_FETCH_SUB_16};
174201095a5dSDimitry Andric static const RTLIB::Libcall LibcallsAnd[6] = {
174301095a5dSDimitry Andric RTLIB::UNKNOWN_LIBCALL, RTLIB::ATOMIC_FETCH_AND_1,
174401095a5dSDimitry Andric RTLIB::ATOMIC_FETCH_AND_2, RTLIB::ATOMIC_FETCH_AND_4,
174501095a5dSDimitry Andric RTLIB::ATOMIC_FETCH_AND_8, RTLIB::ATOMIC_FETCH_AND_16};
174601095a5dSDimitry Andric static const RTLIB::Libcall LibcallsOr[6] = {
174701095a5dSDimitry Andric RTLIB::UNKNOWN_LIBCALL, RTLIB::ATOMIC_FETCH_OR_1,
174801095a5dSDimitry Andric RTLIB::ATOMIC_FETCH_OR_2, RTLIB::ATOMIC_FETCH_OR_4,
174901095a5dSDimitry Andric RTLIB::ATOMIC_FETCH_OR_8, RTLIB::ATOMIC_FETCH_OR_16};
175001095a5dSDimitry Andric static const RTLIB::Libcall LibcallsXor[6] = {
175101095a5dSDimitry Andric RTLIB::UNKNOWN_LIBCALL, RTLIB::ATOMIC_FETCH_XOR_1,
175201095a5dSDimitry Andric RTLIB::ATOMIC_FETCH_XOR_2, RTLIB::ATOMIC_FETCH_XOR_4,
175301095a5dSDimitry Andric RTLIB::ATOMIC_FETCH_XOR_8, RTLIB::ATOMIC_FETCH_XOR_16};
175401095a5dSDimitry Andric static const RTLIB::Libcall LibcallsNand[6] = {
175501095a5dSDimitry Andric RTLIB::UNKNOWN_LIBCALL, RTLIB::ATOMIC_FETCH_NAND_1,
175601095a5dSDimitry Andric RTLIB::ATOMIC_FETCH_NAND_2, RTLIB::ATOMIC_FETCH_NAND_4,
175701095a5dSDimitry Andric RTLIB::ATOMIC_FETCH_NAND_8, RTLIB::ATOMIC_FETCH_NAND_16};
175801095a5dSDimitry Andric
175901095a5dSDimitry Andric switch (Op) {
176001095a5dSDimitry Andric case AtomicRMWInst::BAD_BINOP:
176101095a5dSDimitry Andric llvm_unreachable("Should not have BAD_BINOP.");
176201095a5dSDimitry Andric case AtomicRMWInst::Xchg:
1763e3b55780SDimitry Andric return ArrayRef(LibcallsXchg);
176401095a5dSDimitry Andric case AtomicRMWInst::Add:
1765e3b55780SDimitry Andric return ArrayRef(LibcallsAdd);
176601095a5dSDimitry Andric case AtomicRMWInst::Sub:
1767e3b55780SDimitry Andric return ArrayRef(LibcallsSub);
176801095a5dSDimitry Andric case AtomicRMWInst::And:
1769e3b55780SDimitry Andric return ArrayRef(LibcallsAnd);
177001095a5dSDimitry Andric case AtomicRMWInst::Or:
1771e3b55780SDimitry Andric return ArrayRef(LibcallsOr);
177201095a5dSDimitry Andric case AtomicRMWInst::Xor:
1773e3b55780SDimitry Andric return ArrayRef(LibcallsXor);
177401095a5dSDimitry Andric case AtomicRMWInst::Nand:
1775e3b55780SDimitry Andric return ArrayRef(LibcallsNand);
177601095a5dSDimitry Andric case AtomicRMWInst::Max:
177701095a5dSDimitry Andric case AtomicRMWInst::Min:
177801095a5dSDimitry Andric case AtomicRMWInst::UMax:
177901095a5dSDimitry Andric case AtomicRMWInst::UMin:
17801f917f69SDimitry Andric case AtomicRMWInst::FMax:
17811f917f69SDimitry Andric case AtomicRMWInst::FMin:
1782e6d15924SDimitry Andric case AtomicRMWInst::FAdd:
1783e6d15924SDimitry Andric case AtomicRMWInst::FSub:
1784e3b55780SDimitry Andric case AtomicRMWInst::UIncWrap:
1785e3b55780SDimitry Andric case AtomicRMWInst::UDecWrap:
178601095a5dSDimitry Andric // No atomic libcalls are available for max/min/umax/umin.
178701095a5dSDimitry Andric return {};
178801095a5dSDimitry Andric }
178901095a5dSDimitry Andric llvm_unreachable("Unexpected AtomicRMW operation.");
179001095a5dSDimitry Andric }
179101095a5dSDimitry Andric
expandAtomicRMWToLibcall(AtomicRMWInst * I)1792ac9a064cSDimitry Andric void AtomicExpandImpl::expandAtomicRMWToLibcall(AtomicRMWInst *I) {
179301095a5dSDimitry Andric ArrayRef<RTLIB::Libcall> Libcalls = GetRMWLibcall(I->getOperation());
179401095a5dSDimitry Andric
179501095a5dSDimitry Andric unsigned Size = getAtomicOpSize(I);
179601095a5dSDimitry Andric
179701095a5dSDimitry Andric bool Success = false;
179801095a5dSDimitry Andric if (!Libcalls.empty())
179901095a5dSDimitry Andric Success = expandAtomicOpToLibcall(
1800cfca06d7SDimitry Andric I, Size, I->getAlign(), I->getPointerOperand(), I->getValOperand(),
1801cfca06d7SDimitry Andric nullptr, I->getOrdering(), AtomicOrdering::NotAtomic, Libcalls);
180201095a5dSDimitry Andric
180301095a5dSDimitry Andric // The expansion failed: either there were no libcalls at all for
180401095a5dSDimitry Andric // the operation (min/max), or there were only size-specialized
180501095a5dSDimitry Andric // libcalls (add/sub/etc) and we needed a generic. So, expand to a
180601095a5dSDimitry Andric // CAS libcall, via a CAS loop, instead.
180701095a5dSDimitry Andric if (!Success) {
1808344a3780SDimitry Andric expandAtomicRMWToCmpXchg(
1809e3b55780SDimitry Andric I, [this](IRBuilderBase &Builder, Value *Addr, Value *Loaded,
1810344a3780SDimitry Andric Value *NewVal, Align Alignment, AtomicOrdering MemOpOrder,
1811344a3780SDimitry Andric SyncScope::ID SSID, Value *&Success, Value *&NewLoaded) {
181201095a5dSDimitry Andric // Create the CAS instruction normally...
181301095a5dSDimitry Andric AtomicCmpXchgInst *Pair = Builder.CreateAtomicCmpXchg(
1814344a3780SDimitry Andric Addr, Loaded, NewVal, Alignment, MemOpOrder,
1815344a3780SDimitry Andric AtomicCmpXchgInst::getStrongestFailureOrdering(MemOpOrder), SSID);
181601095a5dSDimitry Andric Success = Builder.CreateExtractValue(Pair, 1, "success");
181701095a5dSDimitry Andric NewLoaded = Builder.CreateExtractValue(Pair, 0, "newloaded");
181801095a5dSDimitry Andric
181901095a5dSDimitry Andric // ...and then expand the CAS into a libcall.
182001095a5dSDimitry Andric expandAtomicCASToLibcall(Pair);
182101095a5dSDimitry Andric });
182201095a5dSDimitry Andric }
182301095a5dSDimitry Andric }
182401095a5dSDimitry Andric
182501095a5dSDimitry Andric // A helper routine for the above expandAtomic*ToLibcall functions.
182601095a5dSDimitry Andric //
182701095a5dSDimitry Andric // 'Libcalls' contains an array of enum values for the particular
182801095a5dSDimitry Andric // ATOMIC libcalls to be emitted. All of the other arguments besides
182901095a5dSDimitry Andric // 'I' are extracted from the Instruction subclass by the
183001095a5dSDimitry Andric // caller. Depending on the particular call, some will be null.
expandAtomicOpToLibcall(Instruction * I,unsigned Size,Align Alignment,Value * PointerOperand,Value * ValueOperand,Value * CASExpected,AtomicOrdering Ordering,AtomicOrdering Ordering2,ArrayRef<RTLIB::Libcall> Libcalls)1831ac9a064cSDimitry Andric bool AtomicExpandImpl::expandAtomicOpToLibcall(
1832cfca06d7SDimitry Andric Instruction *I, unsigned Size, Align Alignment, Value *PointerOperand,
183301095a5dSDimitry Andric Value *ValueOperand, Value *CASExpected, AtomicOrdering Ordering,
183401095a5dSDimitry Andric AtomicOrdering Ordering2, ArrayRef<RTLIB::Libcall> Libcalls) {
183501095a5dSDimitry Andric assert(Libcalls.size() == 6);
183601095a5dSDimitry Andric
183701095a5dSDimitry Andric LLVMContext &Ctx = I->getContext();
183801095a5dSDimitry Andric Module *M = I->getModule();
183901095a5dSDimitry Andric const DataLayout &DL = M->getDataLayout();
184001095a5dSDimitry Andric IRBuilder<> Builder(I);
184101095a5dSDimitry Andric IRBuilder<> AllocaBuilder(&I->getFunction()->getEntryBlock().front());
184201095a5dSDimitry Andric
1843cfca06d7SDimitry Andric bool UseSizedLibcall = canUseSizedAtomicCall(Size, Alignment, DL);
184401095a5dSDimitry Andric Type *SizedIntTy = Type::getIntNTy(Ctx, Size * 8);
184501095a5dSDimitry Andric
1846cfca06d7SDimitry Andric const Align AllocaAlignment = DL.getPrefTypeAlign(SizedIntTy);
184701095a5dSDimitry Andric
184801095a5dSDimitry Andric // TODO: the "order" argument type is "int", not int32. So
184901095a5dSDimitry Andric // getInt32Ty may be wrong if the arch uses e.g. 16-bit ints.
185001095a5dSDimitry Andric ConstantInt *SizeVal64 = ConstantInt::get(Type::getInt64Ty(Ctx), Size);
185101095a5dSDimitry Andric assert(Ordering != AtomicOrdering::NotAtomic && "expect atomic MO");
185201095a5dSDimitry Andric Constant *OrderingVal =
185301095a5dSDimitry Andric ConstantInt::get(Type::getInt32Ty(Ctx), (int)toCABI(Ordering));
185401095a5dSDimitry Andric Constant *Ordering2Val = nullptr;
185501095a5dSDimitry Andric if (CASExpected) {
185601095a5dSDimitry Andric assert(Ordering2 != AtomicOrdering::NotAtomic && "expect atomic MO");
185701095a5dSDimitry Andric Ordering2Val =
185801095a5dSDimitry Andric ConstantInt::get(Type::getInt32Ty(Ctx), (int)toCABI(Ordering2));
185901095a5dSDimitry Andric }
186001095a5dSDimitry Andric bool HasResult = I->getType() != Type::getVoidTy(Ctx);
186101095a5dSDimitry Andric
186201095a5dSDimitry Andric RTLIB::Libcall RTLibType;
186301095a5dSDimitry Andric if (UseSizedLibcall) {
186401095a5dSDimitry Andric switch (Size) {
1865145449b1SDimitry Andric case 1:
1866145449b1SDimitry Andric RTLibType = Libcalls[1];
1867145449b1SDimitry Andric break;
1868145449b1SDimitry Andric case 2:
1869145449b1SDimitry Andric RTLibType = Libcalls[2];
1870145449b1SDimitry Andric break;
1871145449b1SDimitry Andric case 4:
1872145449b1SDimitry Andric RTLibType = Libcalls[3];
1873145449b1SDimitry Andric break;
1874145449b1SDimitry Andric case 8:
1875145449b1SDimitry Andric RTLibType = Libcalls[4];
1876145449b1SDimitry Andric break;
1877145449b1SDimitry Andric case 16:
1878145449b1SDimitry Andric RTLibType = Libcalls[5];
1879145449b1SDimitry Andric break;
188001095a5dSDimitry Andric }
188101095a5dSDimitry Andric } else if (Libcalls[0] != RTLIB::UNKNOWN_LIBCALL) {
188201095a5dSDimitry Andric RTLibType = Libcalls[0];
188301095a5dSDimitry Andric } else {
188401095a5dSDimitry Andric // Can't use sized function, and there's no generic for this
188501095a5dSDimitry Andric // operation, so give up.
188601095a5dSDimitry Andric return false;
188701095a5dSDimitry Andric }
188801095a5dSDimitry Andric
1889b60736ecSDimitry Andric if (!TLI->getLibcallName(RTLibType)) {
1890b60736ecSDimitry Andric // This target does not implement the requested atomic libcall so give up.
1891b60736ecSDimitry Andric return false;
1892b60736ecSDimitry Andric }
1893b60736ecSDimitry Andric
189401095a5dSDimitry Andric // Build up the function call. There's two kinds. First, the sized
189501095a5dSDimitry Andric // variants. These calls are going to be one of the following (with
189601095a5dSDimitry Andric // N=1,2,4,8,16):
189701095a5dSDimitry Andric // iN __atomic_load_N(iN *ptr, int ordering)
189801095a5dSDimitry Andric // void __atomic_store_N(iN *ptr, iN val, int ordering)
189901095a5dSDimitry Andric // iN __atomic_{exchange|fetch_*}_N(iN *ptr, iN val, int ordering)
190001095a5dSDimitry Andric // bool __atomic_compare_exchange_N(iN *ptr, iN *expected, iN desired,
190101095a5dSDimitry Andric // int success_order, int failure_order)
190201095a5dSDimitry Andric //
190301095a5dSDimitry Andric // Note that these functions can be used for non-integer atomic
190401095a5dSDimitry Andric // operations, the values just need to be bitcast to integers on the
190501095a5dSDimitry Andric // way in and out.
190601095a5dSDimitry Andric //
190701095a5dSDimitry Andric // And, then, the generic variants. They look like the following:
190801095a5dSDimitry Andric // void __atomic_load(size_t size, void *ptr, void *ret, int ordering)
190901095a5dSDimitry Andric // void __atomic_store(size_t size, void *ptr, void *val, int ordering)
191001095a5dSDimitry Andric // void __atomic_exchange(size_t size, void *ptr, void *val, void *ret,
191101095a5dSDimitry Andric // int ordering)
191201095a5dSDimitry Andric // bool __atomic_compare_exchange(size_t size, void *ptr, void *expected,
191301095a5dSDimitry Andric // void *desired, int success_order,
191401095a5dSDimitry Andric // int failure_order)
191501095a5dSDimitry Andric //
191601095a5dSDimitry Andric // The different signatures are built up depending on the
191701095a5dSDimitry Andric // 'UseSizedLibcall', 'CASExpected', 'ValueOperand', and 'HasResult'
191801095a5dSDimitry Andric // variables.
191901095a5dSDimitry Andric
192001095a5dSDimitry Andric AllocaInst *AllocaCASExpected = nullptr;
192101095a5dSDimitry Andric AllocaInst *AllocaValue = nullptr;
192201095a5dSDimitry Andric AllocaInst *AllocaResult = nullptr;
192301095a5dSDimitry Andric
192401095a5dSDimitry Andric Type *ResultTy;
192501095a5dSDimitry Andric SmallVector<Value *, 6> Args;
192671d5a254SDimitry Andric AttributeList Attr;
192701095a5dSDimitry Andric
192801095a5dSDimitry Andric // 'size' argument.
192901095a5dSDimitry Andric if (!UseSizedLibcall) {
193001095a5dSDimitry Andric // Note, getIntPtrType is assumed equivalent to size_t.
193101095a5dSDimitry Andric Args.push_back(ConstantInt::get(DL.getIntPtrType(Ctx), Size));
193201095a5dSDimitry Andric }
193301095a5dSDimitry Andric
193401095a5dSDimitry Andric // 'ptr' argument.
1935e6d15924SDimitry Andric // note: This assumes all address spaces share a common libfunc
1936e6d15924SDimitry Andric // implementation and that addresses are convertable. For systems without
1937e6d15924SDimitry Andric // that property, we'd need to extend this mechanism to support AS-specific
1938e6d15924SDimitry Andric // families of atomic intrinsics.
1939b1c73532SDimitry Andric Value *PtrVal = PointerOperand;
1940b1c73532SDimitry Andric PtrVal = Builder.CreateAddrSpaceCast(PtrVal, PointerType::getUnqual(Ctx));
194101095a5dSDimitry Andric Args.push_back(PtrVal);
194201095a5dSDimitry Andric
194301095a5dSDimitry Andric // 'expected' argument, if present.
194401095a5dSDimitry Andric if (CASExpected) {
194501095a5dSDimitry Andric AllocaCASExpected = AllocaBuilder.CreateAlloca(CASExpected->getType());
1946cfca06d7SDimitry Andric AllocaCASExpected->setAlignment(AllocaAlignment);
1947b1c73532SDimitry Andric Builder.CreateLifetimeStart(AllocaCASExpected, SizeVal64);
194801095a5dSDimitry Andric Builder.CreateAlignedStore(CASExpected, AllocaCASExpected, AllocaAlignment);
1949b1c73532SDimitry Andric Args.push_back(AllocaCASExpected);
195001095a5dSDimitry Andric }
195101095a5dSDimitry Andric
195201095a5dSDimitry Andric // 'val' argument ('desired' for cas), if present.
195301095a5dSDimitry Andric if (ValueOperand) {
195401095a5dSDimitry Andric if (UseSizedLibcall) {
195501095a5dSDimitry Andric Value *IntValue =
195601095a5dSDimitry Andric Builder.CreateBitOrPointerCast(ValueOperand, SizedIntTy);
195701095a5dSDimitry Andric Args.push_back(IntValue);
195801095a5dSDimitry Andric } else {
195901095a5dSDimitry Andric AllocaValue = AllocaBuilder.CreateAlloca(ValueOperand->getType());
1960cfca06d7SDimitry Andric AllocaValue->setAlignment(AllocaAlignment);
1961b1c73532SDimitry Andric Builder.CreateLifetimeStart(AllocaValue, SizeVal64);
196201095a5dSDimitry Andric Builder.CreateAlignedStore(ValueOperand, AllocaValue, AllocaAlignment);
1963b1c73532SDimitry Andric Args.push_back(AllocaValue);
196401095a5dSDimitry Andric }
196501095a5dSDimitry Andric }
196601095a5dSDimitry Andric
196701095a5dSDimitry Andric // 'ret' argument.
196801095a5dSDimitry Andric if (!CASExpected && HasResult && !UseSizedLibcall) {
196901095a5dSDimitry Andric AllocaResult = AllocaBuilder.CreateAlloca(I->getType());
1970cfca06d7SDimitry Andric AllocaResult->setAlignment(AllocaAlignment);
1971b1c73532SDimitry Andric Builder.CreateLifetimeStart(AllocaResult, SizeVal64);
1972b1c73532SDimitry Andric Args.push_back(AllocaResult);
197301095a5dSDimitry Andric }
197401095a5dSDimitry Andric
197501095a5dSDimitry Andric // 'ordering' ('success_order' for cas) argument.
197601095a5dSDimitry Andric Args.push_back(OrderingVal);
197701095a5dSDimitry Andric
197801095a5dSDimitry Andric // 'failure_order' argument, if present.
197901095a5dSDimitry Andric if (Ordering2Val)
198001095a5dSDimitry Andric Args.push_back(Ordering2Val);
198101095a5dSDimitry Andric
198201095a5dSDimitry Andric // Now, the return type.
198301095a5dSDimitry Andric if (CASExpected) {
198401095a5dSDimitry Andric ResultTy = Type::getInt1Ty(Ctx);
1985c0981da4SDimitry Andric Attr = Attr.addRetAttribute(Ctx, Attribute::ZExt);
198601095a5dSDimitry Andric } else if (HasResult && UseSizedLibcall)
198701095a5dSDimitry Andric ResultTy = SizedIntTy;
198801095a5dSDimitry Andric else
198901095a5dSDimitry Andric ResultTy = Type::getVoidTy(Ctx);
199001095a5dSDimitry Andric
199101095a5dSDimitry Andric // Done with setting up arguments and return types, create the call:
199201095a5dSDimitry Andric SmallVector<Type *, 6> ArgTys;
199301095a5dSDimitry Andric for (Value *Arg : Args)
199401095a5dSDimitry Andric ArgTys.push_back(Arg->getType());
199501095a5dSDimitry Andric FunctionType *FnType = FunctionType::get(ResultTy, ArgTys, false);
1996e6d15924SDimitry Andric FunctionCallee LibcallFn =
199701095a5dSDimitry Andric M->getOrInsertFunction(TLI->getLibcallName(RTLibType), FnType, Attr);
199801095a5dSDimitry Andric CallInst *Call = Builder.CreateCall(LibcallFn, Args);
199901095a5dSDimitry Andric Call->setAttributes(Attr);
200001095a5dSDimitry Andric Value *Result = Call;
200101095a5dSDimitry Andric
200201095a5dSDimitry Andric // And then, extract the results...
200301095a5dSDimitry Andric if (ValueOperand && !UseSizedLibcall)
2004b1c73532SDimitry Andric Builder.CreateLifetimeEnd(AllocaValue, SizeVal64);
200501095a5dSDimitry Andric
200601095a5dSDimitry Andric if (CASExpected) {
200701095a5dSDimitry Andric // The final result from the CAS is {load of 'expected' alloca, bool result
200801095a5dSDimitry Andric // from call}
200901095a5dSDimitry Andric Type *FinalResultTy = I->getType();
2010e3b55780SDimitry Andric Value *V = PoisonValue::get(FinalResultTy);
2011e6d15924SDimitry Andric Value *ExpectedOut = Builder.CreateAlignedLoad(
2012e6d15924SDimitry Andric CASExpected->getType(), AllocaCASExpected, AllocaAlignment);
2013b1c73532SDimitry Andric Builder.CreateLifetimeEnd(AllocaCASExpected, SizeVal64);
201401095a5dSDimitry Andric V = Builder.CreateInsertValue(V, ExpectedOut, 0);
201501095a5dSDimitry Andric V = Builder.CreateInsertValue(V, Result, 1);
201601095a5dSDimitry Andric I->replaceAllUsesWith(V);
201701095a5dSDimitry Andric } else if (HasResult) {
201801095a5dSDimitry Andric Value *V;
201901095a5dSDimitry Andric if (UseSizedLibcall)
202001095a5dSDimitry Andric V = Builder.CreateBitOrPointerCast(Result, I->getType());
202101095a5dSDimitry Andric else {
2022e6d15924SDimitry Andric V = Builder.CreateAlignedLoad(I->getType(), AllocaResult,
2023e6d15924SDimitry Andric AllocaAlignment);
2024b1c73532SDimitry Andric Builder.CreateLifetimeEnd(AllocaResult, SizeVal64);
202501095a5dSDimitry Andric }
202601095a5dSDimitry Andric I->replaceAllUsesWith(V);
202701095a5dSDimitry Andric }
202801095a5dSDimitry Andric I->eraseFromParent();
2029dd58ef01SDimitry Andric return true;
2030dd58ef01SDimitry Andric }
2031