4949#include " llvm/Support/Debug.h"
5050
5151#include < set>
52+ #include < vector>
5253
5354using namespace llvm ;
5455using namespace SPIRV ;
@@ -112,17 +113,18 @@ bool SPIRVRegularizeLLVM::regularize() {
112113 continue ;
113114 }
114115
115- for (auto BI = F->begin (), BE = F->end (); BI != BE; ++BI) {
116- for (auto II = BI->begin (), IE = BI->end (); II != IE; ++II) {
117- if (auto Call = dyn_cast<CallInst>(II)) {
116+ std::vector<Instruction *> ToErase;
117+ for (BasicBlock &BB : *F) {
118+ for (Instruction &II : BB) {
119+ if (auto Call = dyn_cast<CallInst>(&II)) {
118120 Call->setTailCall (false );
119121 Function *CF = Call->getCalledFunction ();
120122 if (CF && CF->isIntrinsic ())
121123 removeFnAttr (Call, Attribute::NoUnwind);
122124 }
123125
124126 // Remove optimization info not supported by SPIRV
125- if (auto BO = dyn_cast<BinaryOperator>(II)) {
127+ if (auto BO = dyn_cast<BinaryOperator>(& II)) {
126128 if (isa<PossiblyExactOperator>(BO) && BO->isExact ())
127129 BO->setIsExact (false );
128130 }
@@ -133,12 +135,68 @@ bool SPIRVRegularizeLLVM::regularize() {
133135 " range" ,
134136 };
135137 for (auto &MDName : MDs) {
136- if (II-> getMetadata (MDName)) {
137- II-> setMetadata (MDName, nullptr );
138+ if (II. getMetadata (MDName)) {
139+ II. setMetadata (MDName, nullptr );
138140 }
139141 }
142+ if (auto Cmpxchg = dyn_cast<AtomicCmpXchgInst>(&II)) {
143+ Value *Ptr = Cmpxchg->getPointerOperand ();
144+ // To get memory scope argument we might use Cmpxchg->getSyncScopeID()
145+ // but LLVM's cmpxchg instruction is not aware of OpenCL(or SPIR-V)
146+ // memory scope enumeration. And assuming the produced SPIR-V module
147+ // will be consumed in an OpenCL environment, we can use the same
148+ // memory scope as OpenCL atomic functions that do not have
149+ // memory_scope argument, i.e. memory_scope_device. See the OpenCL C
150+ // specification p6.13.11. Atomic Functions
151+ Value *MemoryScope = getInt32 (M, spv::ScopeDevice);
152+ auto SuccessOrder = static_cast <OCLMemOrderKind>(
153+ llvm::toCABI (Cmpxchg->getSuccessOrdering ()));
154+ auto FailureOrder = static_cast <OCLMemOrderKind>(
155+ llvm::toCABI (Cmpxchg->getFailureOrdering ()));
156+ Value *EqualSem = getInt32 (M, OCLMemOrderMap::map (SuccessOrder));
157+ Value *UnequalSem = getInt32 (M, OCLMemOrderMap::map (FailureOrder));
158+ Value *Val = Cmpxchg->getNewValOperand ();
159+ Value *Comparator = Cmpxchg->getCompareOperand ();
160+
161+ llvm::Value *Args[] = {Ptr, MemoryScope, EqualSem,
162+ UnequalSem, Val, Comparator};
163+ auto *Res = addCallInstSPIRV (M, " __spirv_AtomicCompareExchange" ,
164+ Cmpxchg->getCompareOperand ()->getType (),
165+ Args, nullptr , &II, " cmpxchg.res" );
166+ // cmpxchg LLVM instruction returns a pair: the original value and
167+ // a flag indicating success (true) or failure (false).
168+ // OpAtomicCompareExchange SPIR-V instruction returns only the
169+ // original value. So we replace all uses of the original value
170+ // extracted from the pair with the result of OpAtomicCompareExchange
171+ // instruction. And we replace all uses of the flag with result of an
172+ // OpIEqual instruction. The OpIEqual instruction returns true if the
173+ // original value equals to the comparator which matches with
174+ // semantics of cmpxchg.
175+ for (User *U : Cmpxchg->users ()) {
176+ if (auto *Extract = dyn_cast<ExtractValueInst>(U)) {
177+ if (Extract->getIndices ()[0 ] == 0 ) {
178+ Extract->replaceAllUsesWith (Res);
179+ } else if (Extract->getIndices ()[0 ] == 1 ) {
180+ auto *Cmp = new ICmpInst (Extract, CmpInst::ICMP_EQ, Res,
181+ Comparator, " cmpxchg.success" );
182+ Extract->replaceAllUsesWith (Cmp);
183+ } else {
184+ llvm_unreachable (" Unxpected cmpxchg pattern" );
185+ }
186+ assert (Extract->user_empty ());
187+ Extract->dropAllReferences ();
188+ ToErase.push_back (Extract);
189+ }
190+ }
191+ if (Cmpxchg->user_empty ())
192+ ToErase.push_back (Cmpxchg);
193+ }
140194 }
141195 }
196+ for (Instruction *V : ToErase) {
197+ assert (V->user_empty ());
198+ V->eraseFromParent ();
199+ }
142200 }
143201
144202 std::string Err;
0 commit comments