49
49
#include " llvm/Support/Debug.h"
50
50
51
51
#include < set>
52
+ #include < vector>
52
53
53
54
using namespace llvm ;
54
55
using namespace SPIRV ;
@@ -112,17 +113,18 @@ bool SPIRVRegularizeLLVM::regularize() {
112
113
continue ;
113
114
}
114
115
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)) {
118
120
Call->setTailCall (false );
119
121
Function *CF = Call->getCalledFunction ();
120
122
if (CF && CF->isIntrinsic ())
121
123
removeFnAttr (Call, Attribute::NoUnwind);
122
124
}
123
125
124
126
// Remove optimization info not supported by SPIRV
125
- if (auto BO = dyn_cast<BinaryOperator>(II)) {
127
+ if (auto BO = dyn_cast<BinaryOperator>(& II)) {
126
128
if (isa<PossiblyExactOperator>(BO) && BO->isExact ())
127
129
BO->setIsExact (false );
128
130
}
@@ -133,12 +135,68 @@ bool SPIRVRegularizeLLVM::regularize() {
133
135
" range" ,
134
136
};
135
137
for (auto &MDName : MDs) {
136
- if (II-> getMetadata (MDName)) {
137
- II-> setMetadata (MDName, nullptr );
138
+ if (II. getMetadata (MDName)) {
139
+ II. setMetadata (MDName, nullptr );
138
140
}
139
141
}
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
+ }
140
194
}
141
195
}
196
+ for (Instruction *V : ToErase) {
197
+ assert (V->user_empty ());
198
+ V->eraseFromParent ();
199
+ }
142
200
}
143
201
144
202
std::string Err;
0 commit comments