Skip to content

Commit 2a899ba

Browse files
committed
Fix translation of cmpxchg instruction
Previous implementation optimized produced code assuming a particular pattern in input LLVM IR. But that assumption is not always correct. Now we always insert extra instructions to preserve semantics of cmpxchg. Those extra instruction can be optimized later. Signed-off-by: Alexey Sotkin <alexey.sotkin@intel.com>
1 parent 8f01260 commit 2a899ba

File tree

2 files changed

+31
-43
lines changed

2 files changed

+31
-43
lines changed

lib/SPIRV/SPIRVRegularizeLLVM.cpp

Lines changed: 28 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -309,14 +309,34 @@ bool SPIRVRegularizeLLVM::regularize() {
309309
}
310310
}
311311
if (auto Cmpxchg = dyn_cast<AtomicCmpXchgInst>(&II)) {
312-
Value *Ptr = Cmpxchg->getPointerOperand();
312+
// Transform:
313+
// %1 = cmpxchg i32* %ptr, i32 %comparator, i32 %0 seq_cst acquire
314+
// To:
315+
// %cmpxchg.res = call spir_func
316+
// i32 @_Z29__spirv_AtomicCompareExchangePiiiiii(
317+
// i32* %ptr, i32 1, i32 16, i32 2, i32 %0, i32 %comparator)
318+
// %cmpxchg.success = icmp eq i32 %cmpxchg.res, %comparator
319+
// %1 = insertvalue { i32, i1 } undef, i32 %cmpxchg.res, 0
320+
// %2 = insertvalue { i32, i1 } %1, i1 %cmpxchg.success, 1
321+
313322
// To get memory scope argument we might use Cmpxchg->getSyncScopeID()
314323
// but LLVM's cmpxchg instruction is not aware of OpenCL(or SPIR-V)
315324
// memory scope enumeration. And assuming the produced SPIR-V module
316325
// will be consumed in an OpenCL environment, we can use the same
317326
// memory scope as OpenCL atomic functions that do not have
318327
// memory_scope argument, i.e. memory_scope_device. See the OpenCL C
319328
// specification p6.13.11. Atomic Functions
329+
330+
// cmpxchg LLVM instruction returns a pair {i32, i1}: the original
331+
// value and a flag indicating success (true) or failure (false).
332+
// OpAtomicCompareExchange SPIR-V instruction returns only the
333+
// original value. To keep the return type({i32, i1}) we construct
334+
// a composite. The first element of the composite holds result of
335+
// OpAtomicCompareExchange, i.e. the original value. The second
336+
// element holds result of comparison of the returned value and the
337+
// comparator, which matches with semantics of the flag returned by
338+
// cmpxchg.
339+
Value *Ptr = Cmpxchg->getPointerOperand();
320340
Value *MemoryScope = getInt32(M, spv::ScopeDevice);
321341
auto SuccessOrder = static_cast<OCLMemOrderKind>(
322342
llvm::toCABI(Cmpxchg->getSuccessOrdering()));
@@ -332,45 +352,13 @@ bool SPIRVRegularizeLLVM::regularize() {
332352
auto *Res = addCallInstSPIRV(M, "__spirv_AtomicCompareExchange",
333353
Cmpxchg->getCompareOperand()->getType(),
334354
Args, nullptr, &II, "cmpxchg.res");
335-
// cmpxchg LLVM instruction returns a pair: the original value and
336-
// a flag indicating success (true) or failure (false).
337-
// OpAtomicCompareExchange SPIR-V instruction returns only the
338-
// original value. So we replace all uses of the original value
339-
// extracted from the pair with the result of OpAtomicCompareExchange
340-
// instruction. And we replace all uses of the flag with result of an
341-
// OpIEqual instruction. The OpIEqual instruction returns true if the
342-
// original value equals to the comparator which matches with
343-
// semantics of cmpxchg.
344-
// In case the original value was stored as is without extraction, we
345-
// create a composite type manually from OpAtomicCompareExchange and
346-
// OpIEqual instructions, and replace the original value usage in
347-
// Store insruction with the new composite type.
348-
for (User *U : Cmpxchg->users()) {
349-
if (auto *Extract = dyn_cast<ExtractValueInst>(U)) {
350-
if (Extract->getIndices()[0] == 0) {
351-
Extract->replaceAllUsesWith(Res);
352-
} else if (Extract->getIndices()[0] == 1) {
353-
auto *Cmp = new ICmpInst(Extract, CmpInst::ICMP_EQ, Res,
354-
Comparator, "cmpxchg.success");
355-
Extract->replaceAllUsesWith(Cmp);
356-
} else {
357-
llvm_unreachable("Unxpected cmpxchg pattern");
358-
}
359-
assert(Extract->user_empty());
360-
Extract->dropAllReferences();
361-
ToErase.push_back(Extract);
362-
} else if (auto *Store = dyn_cast<StoreInst>(U)) {
363-
auto *Cmp = new ICmpInst(Store, CmpInst::ICMP_EQ, Res, Comparator,
364-
"cmpxchg.success");
365-
auto *Agg = InsertValueInst::Create(
366-
UndefValue::get(Cmpxchg->getType()), Res, 0, "agg0", Store);
367-
auto *AggStruct =
368-
InsertValueInst::Create(Agg, Cmp, 1, "agg1", Store);
369-
Store->getValueOperand()->replaceAllUsesWith(AggStruct);
370-
}
371-
}
372-
if (Cmpxchg->user_empty())
373-
ToErase.push_back(Cmpxchg);
355+
IRBuilder<> Builder(Cmpxchg);
356+
auto *Cmp = Builder.CreateICmpEQ(Res, Comparator, "cmpxchg.success");
357+
auto *V1 = Builder.CreateInsertValue(
358+
UndefValue::get(Cmpxchg->getType()), Res, 0);
359+
auto *V2 = Builder.CreateInsertValue(V1, Cmp, 1, Cmpxchg->getName());
360+
Cmpxchg->replaceAllUsesWith(V2);
361+
ToErase.push_back(Cmpxchg);
374362
}
375363
}
376364
}

test/AtomicCompareExchange.ll

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@
2121
; CHECK-SPIRV: AtomicCompareExchange [[Int]] [[Res:[0-9]+]] [[Pointer]] [[MemScope_Device]]
2222
; CHECK-SPIRV-SAME: [[MemSemEqual_SeqCst]] [[MemSemUnequal_Acquire]] [[Value]] [[Comparator]]
2323
; CHECK-SPIRV: IEqual {{[0-9]+}} [[Success:[0-9]+]] [[Res]] [[Comparator]]
24-
; CHECK-SPIRV: BranchConditional [[Success]]
25-
26-
; CHECK-SPIRV: Store [[Value_ptr]] [[Res]]
24+
; CHECK-SPIRV: CompositeInsert [[Struct]] [[Composite_0:[0-9]+]] [[Res]] [[UndefStruct]] 0
25+
; CHECK-SPIRV: CompositeInsert [[Struct]] [[Composite_1:[0-9]+]] [[Success]] [[Composite_0]] 1
26+
; CHECK-SPIRV: CompositeExtract [[Bool]] {{[0-9]+}} [[Composite_1]] 1
2727

2828
target datalayout = "e-p:32:32-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024"
2929
target triple = "spir"

0 commit comments

Comments
 (0)