clang 23.0.0git
CIRGenAtomic.cpp
Go to the documentation of this file.
1//===--- CIRGenAtomic.cpp - Emit CIR for atomic operations ----------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file contains the code for emitting atomic operations.
10//
11//===----------------------------------------------------------------------===//
12
13#include "CIRGenFunction.h"
15
16using namespace clang;
17using namespace clang::CIRGen;
18using namespace cir;
19
20namespace {
21class AtomicInfo {
22 CIRGenFunction &cgf;
23 QualType atomicTy;
24 QualType valueTy;
25 uint64_t atomicSizeInBits = 0;
26 uint64_t valueSizeInBits = 0;
27 CharUnits atomicAlign;
28 CharUnits valueAlign;
29 TypeEvaluationKind evaluationKind = cir::TEK_Scalar;
30 bool useLibCall = true;
31 LValue lvalue;
32 mlir::Location loc;
33
34public:
35 AtomicInfo(CIRGenFunction &cgf, LValue &lvalue, mlir::Location loc)
36 : cgf(cgf), loc(loc) {
37 assert(!lvalue.isGlobalReg());
38 ASTContext &ctx = cgf.getContext();
39 if (lvalue.isSimple()) {
40 atomicTy = lvalue.getType();
41 if (auto *ty = atomicTy->getAs<AtomicType>())
42 valueTy = ty->getValueType();
43 else
44 valueTy = atomicTy;
45 evaluationKind = cgf.getEvaluationKind(valueTy);
46
47 TypeInfo valueTypeInfo = ctx.getTypeInfo(valueTy);
48 TypeInfo atomicTypeInfo = ctx.getTypeInfo(atomicTy);
49 uint64_t valueAlignInBits = valueTypeInfo.Align;
50 uint64_t atomicAlignInBits = atomicTypeInfo.Align;
51 valueSizeInBits = valueTypeInfo.Width;
52 atomicSizeInBits = atomicTypeInfo.Width;
53 assert(valueSizeInBits <= atomicSizeInBits);
54 assert(valueAlignInBits <= atomicAlignInBits);
55
56 atomicAlign = ctx.toCharUnitsFromBits(atomicAlignInBits);
57 valueAlign = ctx.toCharUnitsFromBits(valueAlignInBits);
58 if (lvalue.getAlignment().isZero())
59 lvalue.setAlignment(atomicAlign);
60
61 this->lvalue = lvalue;
62 } else {
64 cgf.cgm.errorNYI(loc, "AtomicInfo: non-simple lvalue");
65 }
66 useLibCall = !ctx.getTargetInfo().hasBuiltinAtomic(
67 atomicSizeInBits, ctx.toBits(lvalue.getAlignment()));
68 }
69
70 QualType getValueType() const { return valueTy; }
71 CharUnits getAtomicAlignment() const { return atomicAlign; }
72 TypeEvaluationKind getEvaluationKind() const { return evaluationKind; }
73 mlir::Value getAtomicPointer() const {
74 if (lvalue.isSimple())
75 return lvalue.getPointer();
77 return nullptr;
78 }
79 bool shouldUseLibCall() const { return useLibCall; }
80 const LValue &getAtomicLValue() const { return lvalue; }
81 Address getAtomicAddress() const {
82 mlir::Type elemTy;
83 if (lvalue.isSimple()) {
84 elemTy = lvalue.getAddress().getElementType();
85 } else {
87 cgf.cgm.errorNYI(loc, "AtomicInfo::getAtomicAddress: non-simple lvalue");
88 }
89 return Address(getAtomicPointer(), elemTy, getAtomicAlignment());
90 }
91
92 /// Is the atomic size larger than the underlying value type?
93 ///
94 /// Note that the absence of padding does not mean that atomic
95 /// objects are completely interchangeable with non-atomic
96 /// objects: we might have promoted the alignment of a type
97 /// without making it bigger.
98 bool hasPadding() const { return (valueSizeInBits != atomicSizeInBits); }
99
100 bool emitMemSetZeroIfNecessary() const;
101
102 mlir::Value getScalarRValValueOrNull(RValue rvalue) const;
103
104 /// Cast the given pointer to an integer pointer suitable for atomic
105 /// operations on the source.
106 Address castToAtomicIntPointer(Address addr) const;
107
108 /// If addr is compatible with the iN that will be used for an atomic
109 /// operation, bitcast it. Otherwise, create a temporary that is suitable and
110 /// copy the value across.
111 Address convertToAtomicIntPointer(Address addr) const;
112
113 /// Converts a rvalue to integer value.
114 mlir::Value convertRValueToInt(RValue rvalue, bool cmpxchg = false) const;
115
116 /// Copy an atomic r-value into atomic-layout memory.
117 void emitCopyIntoMemory(RValue rvalue) const;
118
119 /// Project an l-value down to the value field.
120 LValue projectValue() const {
121 assert(lvalue.isSimple());
122 Address addr = getAtomicAddress();
123 if (hasPadding()) {
124 cgf.cgm.errorNYI(loc, "AtomicInfo::projectValue: padding");
125 }
126
128 return LValue::makeAddr(addr, getValueType(), lvalue.getBaseInfo());
129 }
130
131 /// Creates temp alloca for intermediate operations on atomic value.
132 Address createTempAlloca() const;
133
134private:
135 bool requiresMemSetZero(mlir::Type ty) const;
136};
137} // namespace
138
139// This function emits any expression (scalar, complex, or aggregate)
140// into a temporary alloca.
142 Address declPtr = cgf.createMemTemp(
143 e->getType(), cgf.getLoc(e->getSourceRange()), ".atomictmp");
144 cgf.emitAnyExprToMem(e, declPtr, e->getType().getQualifiers(),
145 /*Init*/ true);
146 return declPtr;
147}
148
149/// Does a store of the given IR type modify the full expected width?
150static bool isFullSizeType(CIRGenModule &cgm, mlir::Type ty,
151 uint64_t expectedSize) {
152 return cgm.getDataLayout().getTypeStoreSize(ty) * 8 == expectedSize;
153}
154
155/// Does the atomic type require memsetting to zero before initialization?
156///
157/// The IR type is provided as a way of making certain queries faster.
158bool AtomicInfo::requiresMemSetZero(mlir::Type ty) const {
159 // If the atomic type has size padding, we definitely need a memset.
160 if (hasPadding())
161 return true;
162
163 // Otherwise, do some simple heuristics to try to avoid it:
164 switch (getEvaluationKind()) {
165 // For scalars and complexes, check whether the store size of the
166 // type uses the full size.
167 case cir::TEK_Scalar:
168 return !isFullSizeType(cgf.cgm, ty, atomicSizeInBits);
169 case cir::TEK_Complex:
170 return !isFullSizeType(cgf.cgm,
171 mlir::cast<cir::ComplexType>(ty).getElementType(),
172 atomicSizeInBits / 2);
173 // Padding in structs has an undefined bit pattern. User beware.
175 return false;
176 }
177 llvm_unreachable("bad evaluation kind");
178}
179
180Address AtomicInfo::convertToAtomicIntPointer(Address addr) const {
181 mlir::Type ty = addr.getElementType();
182 uint64_t sourceSizeInBits = cgf.cgm.getDataLayout().getTypeSizeInBits(ty);
183 if (sourceSizeInBits != atomicSizeInBits) {
184 cgf.cgm.errorNYI(
185 loc,
186 "AtomicInfo::convertToAtomicIntPointer: convert through temp alloca");
187 }
188
189 return castToAtomicIntPointer(addr);
190}
191
192Address AtomicInfo::createTempAlloca() const {
193 Address tempAlloca = cgf.createMemTemp(
194 (lvalue.isBitField() && valueSizeInBits > atomicSizeInBits) ? valueTy
195 : atomicTy,
196 getAtomicAlignment(), loc, "atomic-temp");
197
198 // Cast to pointer to value type for bitfields.
199 if (lvalue.isBitField()) {
200 cgf.cgm.errorNYI(loc, "AtomicInfo::createTempAlloca: bitfield lvalue");
201 }
202
203 return tempAlloca;
204}
205
206mlir::Value AtomicInfo::getScalarRValValueOrNull(RValue rvalue) const {
207 if (rvalue.isScalar() && (!hasPadding() || !lvalue.isSimple()))
208 return rvalue.getValue();
209 return nullptr;
210}
211
212Address AtomicInfo::castToAtomicIntPointer(Address addr) const {
213 auto intTy = mlir::dyn_cast<cir::IntType>(addr.getElementType());
214 // Don't bother with int casts if the integer size is the same.
215 if (intTy && intTy.getWidth() == atomicSizeInBits)
216 return addr;
217 auto ty = cgf.getBuilder().getUIntNTy(atomicSizeInBits);
218 return addr.withElementType(cgf.getBuilder(), ty);
219}
220
221bool AtomicInfo::emitMemSetZeroIfNecessary() const {
222 assert(lvalue.isSimple());
223 Address addr = lvalue.getAddress();
224 if (!requiresMemSetZero(addr.getElementType()))
225 return false;
226
227 cgf.cgm.errorNYI(loc,
228 "AtomicInfo::emitMemSetZeroIfNecaessary: emit memset zero");
229 return false;
230}
231
232/// Return true if \param valueTy is a type that should be casted to integer
233/// around the atomic memory operation. If \param cmpxchg is true, then the
234/// cast of a floating point type is made as that instruction can not have
235/// floating point operands. TODO: Allow compare-and-exchange and FP - see
236/// comment in CIRGenAtomicExpandPass.cpp.
237static bool shouldCastToInt(mlir::Type valueTy, bool cmpxchg) {
238 if (cir::isAnyFloatingPointType(valueTy))
239 return isa<cir::FP80Type>(valueTy) || cmpxchg;
240 return !isa<cir::IntType>(valueTy) && !isa<cir::PointerType>(valueTy);
241}
242
243mlir::Value AtomicInfo::convertRValueToInt(RValue rvalue, bool cmpxchg) const {
244 // If we've got a scalar value of the right size, try to avoid going
245 // through memory. Floats get casted if needed by AtomicExpandPass.
246 if (mlir::Value value = getScalarRValValueOrNull(rvalue)) {
247 if (!shouldCastToInt(value.getType(), cmpxchg))
248 return cgf.emitToMemory(value, valueTy);
249
250 cgf.cgm.errorNYI(
251 loc, "AtomicInfo::convertRValueToInt: cast scalar rvalue to int");
252 return nullptr;
253 }
254
255 cgf.cgm.errorNYI(
256 loc, "AtomicInfo::convertRValueToInt: cast non-scalar rvalue to int");
257 return nullptr;
258}
259
260/// Copy an r-value into memory as part of storing to an atomic type.
261/// This needs to create a bit-pattern suitable for atomic operations.
262void AtomicInfo::emitCopyIntoMemory(RValue rvalue) const {
263 assert(lvalue.isSimple());
264
265 // If we have an r-value, the rvalue should be of the atomic type,
266 // which means that the caller is responsible for having zeroed
267 // any padding. Just do an aggregate copy of that type.
268 if (rvalue.isAggregate()) {
269 cgf.cgm.errorNYI("copying aggregate into atomic lvalue");
270 return;
271 }
272
273 // Okay, otherwise we're copying stuff.
274
275 // Zero out the buffer if necessary.
276 emitMemSetZeroIfNecessary();
277
278 // Drill past the padding if present.
279 LValue tempLValue = projectValue();
280
281 // Okay, store the rvalue in.
282 if (rvalue.isScalar()) {
283 cgf.emitStoreOfScalar(rvalue.getValue(), tempLValue, /*isInit=*/true);
284 } else {
285 cgf.cgm.errorNYI("copying complex into atomic lvalue");
286 }
287}
288
289static void emitDefaultCaseLabel(CIRGenBuilderTy &builder, mlir::Location loc) {
290 mlir::ArrayAttr valuesAttr = builder.getArrayAttr({});
291 mlir::OpBuilder::InsertPoint insertPoint;
292 cir::CaseOp::create(builder, loc, valuesAttr, cir::CaseOpKind::Default,
293 insertPoint);
294 builder.restoreInsertionPoint(insertPoint);
295}
296
297// Create a "case" operation with the given list of orders as its values. Also
298// create the region that will hold the body of the switch-case label.
299static void emitMemOrderCaseLabel(CIRGenBuilderTy &builder, mlir::Location loc,
300 mlir::Type orderType,
303 for (cir::MemOrder order : orders)
304 orderAttrs.push_back(cir::IntAttr::get(orderType, static_cast<int>(order)));
305 mlir::ArrayAttr ordersAttr = builder.getArrayAttr(orderAttrs);
306
307 mlir::OpBuilder::InsertPoint insertPoint;
308 cir::CaseOp::create(builder, loc, ordersAttr, cir::CaseOpKind::Anyof,
309 insertPoint);
310 builder.restoreInsertionPoint(insertPoint);
311}
312
313static void emitAtomicCmpXchg(CIRGenFunction &cgf, AtomicExpr *e, bool isWeak,
314 Address dest, Address ptr, Address val1,
315 Address val2, uint64_t size,
316 cir::MemOrder successOrder,
317 cir::MemOrder failureOrder,
318 cir::SyncScopeKind scope) {
319 mlir::Location loc = cgf.getLoc(e->getSourceRange());
320
321 CIRGenBuilderTy &builder = cgf.getBuilder();
322 mlir::Value expected = builder.createLoad(loc, val1);
323 mlir::Value desired = builder.createLoad(loc, val2);
324
325 auto cmpxchg = cir::AtomicCmpXchgOp::create(
326 builder, loc, expected.getType(), builder.getBoolTy(), ptr.getPointer(),
327 expected, desired,
328 cir::MemOrderAttr::get(&cgf.getMLIRContext(), successOrder),
329 cir::MemOrderAttr::get(&cgf.getMLIRContext(), failureOrder),
330 cir::SyncScopeKindAttr::get(&cgf.getMLIRContext(), scope),
331 builder.getI64IntegerAttr(ptr.getAlignment().getAsAlign().value()));
332
333 cmpxchg.setIsVolatile(e->isVolatile());
334 cmpxchg.setWeak(isWeak);
335
336 mlir::Value failed = builder.createNot(cmpxchg.getSuccess());
337 cir::IfOp::create(builder, loc, failed, /*withElseRegion=*/false,
338 [&](mlir::OpBuilder &, mlir::Location) {
339 auto ptrTy = mlir::cast<cir::PointerType>(
340 val1.getPointer().getType());
341 if (val1.getElementType() != ptrTy.getPointee()) {
342 val1 = val1.withPointer(builder.createPtrBitcast(
343 val1.getPointer(), val1.getElementType()));
344 }
345 builder.createStore(loc, cmpxchg.getOld(), val1);
346 builder.createYield(loc);
347 });
348
349 // Update the memory at Dest with Success's value.
350 cgf.emitStoreOfScalar(cmpxchg.getSuccess(),
351 cgf.makeAddrLValue(dest, e->getType()),
352 /*isInit=*/false);
353}
354
356 bool isWeak, Address dest, Address ptr,
357 Address val1, Address val2,
358 Expr *failureOrderExpr, uint64_t size,
359 cir::MemOrder successOrder,
360 cir::SyncScopeKind scope) {
361 Expr::EvalResult failureOrderEval;
362 if (failureOrderExpr->EvaluateAsInt(failureOrderEval, cgf.getContext())) {
363 uint64_t failureOrderInt = failureOrderEval.Val.getInt().getZExtValue();
364
365 cir::MemOrder failureOrder;
366 if (!cir::isValidCIRAtomicOrderingCABI(failureOrderInt)) {
367 failureOrder = cir::MemOrder::Relaxed;
368 } else {
369 switch ((cir::MemOrder)failureOrderInt) {
370 case cir::MemOrder::Relaxed:
371 // 31.7.2.18: "The failure argument shall not be memory_order_release
372 // nor memory_order_acq_rel". Fallback to monotonic.
373 case cir::MemOrder::Release:
374 case cir::MemOrder::AcquireRelease:
375 failureOrder = cir::MemOrder::Relaxed;
376 break;
377 case cir::MemOrder::Consume:
378 case cir::MemOrder::Acquire:
379 failureOrder = cir::MemOrder::Acquire;
380 break;
381 case cir::MemOrder::SequentiallyConsistent:
382 failureOrder = cir::MemOrder::SequentiallyConsistent;
383 break;
384 }
385 }
386
387 // Prior to c++17, "the failure argument shall be no stronger than the
388 // success argument". This condition has been lifted and the only
389 // precondition is 31.7.2.18. Effectively treat this as a DR and skip
390 // language version checks.
391 emitAtomicCmpXchg(cgf, e, isWeak, dest, ptr, val1, val2, size, successOrder,
392 failureOrder, scope);
393 return;
394 }
395
396 // The failure memory order is not a compile time constant. The CIR atomic ops
397 // require a constant value, so that memory order is known at compile time. In
398 // this case, we can switch based on the memory order and call each variant
399 // individually.
400 mlir::Value failureOrderVal = cgf.emitScalarExpr(failureOrderExpr);
401 mlir::Location atomicLoc = cgf.getLoc(e->getSourceRange());
402 cir::SwitchOp::create(
403 cgf.getBuilder(), atomicLoc, failureOrderVal,
404 [&](mlir::OpBuilder &b, mlir::Location loc, mlir::OperationState &os) {
405 mlir::Block *switchBlock = cgf.getBuilder().getBlock();
406
407 // case cir::MemOrder::Relaxed:
408 // // 31.7.2.18: "The failure argument shall not be
409 // memory_order_release
410 // // nor memory_order_acq_rel". Fallback to monotonic.
411 // case cir::MemOrder::Release:
412 // case cir::MemOrder::AcquireRelease:
413 // Note: Since there are 3 options, this makes sense to just emit as a
414 // 'default', which prevents user code from 'falling off' of this,
415 // which seems reasonable. Also, 'relaxed' being the default behavior
416 // is also probably the least harmful.
417 emitDefaultCaseLabel(cgf.getBuilder(), atomicLoc);
418 emitAtomicCmpXchg(cgf, e, isWeak, dest, ptr, val1, val2, size,
419 successOrder, cir::MemOrder::Relaxed, scope);
420 cgf.getBuilder().createBreak(atomicLoc);
421 cgf.getBuilder().setInsertionPointToEnd(switchBlock);
422
423 // case cir::MemOrder::Consume:
424 // case cir::MemOrder::Acquire:
425 emitMemOrderCaseLabel(cgf.getBuilder(), loc, failureOrderVal.getType(),
426 {cir::MemOrder::Consume, cir::MemOrder::Acquire});
427 emitAtomicCmpXchg(cgf, e, isWeak, dest, ptr, val1, val2, size,
428 successOrder, cir::MemOrder::Acquire, scope);
429 cgf.getBuilder().createBreak(atomicLoc);
430 cgf.getBuilder().setInsertionPointToEnd(switchBlock);
431
432 // case cir::MemOrder::SequentiallyConsistent:
433 emitMemOrderCaseLabel(cgf.getBuilder(), loc, failureOrderVal.getType(),
434 {cir::MemOrder::SequentiallyConsistent});
435 emitAtomicCmpXchg(cgf, e, isWeak, dest, ptr, val1, val2, size,
436 successOrder, cir::MemOrder::SequentiallyConsistent,
437 scope);
438 cgf.getBuilder().createBreak(atomicLoc);
439 cgf.getBuilder().setInsertionPointToEnd(switchBlock);
440
441 cgf.getBuilder().createYield(atomicLoc);
442 });
443}
444
446 Address ptr, Address val1, Address val2,
447 Expr *isWeakExpr, Expr *failureOrderExpr, int64_t size,
448 cir::MemOrder order, cir::SyncScopeKind scope) {
450 llvm::StringRef opName;
451
452 CIRGenBuilderTy &builder = cgf.getBuilder();
453 mlir::Location loc = cgf.getLoc(expr->getSourceRange());
454 auto orderAttr = cir::MemOrderAttr::get(builder.getContext(), order);
455 auto scopeAttr = cir::SyncScopeKindAttr::get(builder.getContext(), scope);
456 cir::AtomicFetchKindAttr fetchAttr;
457 bool fetchFirst = true;
458
459 switch (expr->getOp()) {
460 case AtomicExpr::AO__c11_atomic_init:
461 llvm_unreachable("already handled!");
462
463 case AtomicExpr::AO__c11_atomic_compare_exchange_strong:
464 emitAtomicCmpXchgFailureSet(cgf, expr, /*isWeak=*/false, dest, ptr, val1,
465 val2, failureOrderExpr, size, order, scope);
466 return;
467
468 case AtomicExpr::AO__c11_atomic_compare_exchange_weak:
469 emitAtomicCmpXchgFailureSet(cgf, expr, /*isWeak=*/true, dest, ptr, val1,
470 val2, failureOrderExpr, size, order, scope);
471 return;
472
473 case AtomicExpr::AO__atomic_compare_exchange:
474 case AtomicExpr::AO__atomic_compare_exchange_n:
475 case AtomicExpr::AO__scoped_atomic_compare_exchange:
476 case AtomicExpr::AO__scoped_atomic_compare_exchange_n: {
477 bool isWeak = false;
478 if (isWeakExpr->EvaluateAsBooleanCondition(isWeak, cgf.getContext())) {
479 emitAtomicCmpXchgFailureSet(cgf, expr, isWeak, dest, ptr, val1, val2,
480 failureOrderExpr, size, order, scope);
481 } else {
483 cgf.cgm.errorNYI(expr->getSourceRange(),
484 "emitAtomicOp: non-constant isWeak");
485 }
486 return;
487 }
488
489 case AtomicExpr::AO__c11_atomic_load:
490 case AtomicExpr::AO__atomic_load_n:
491 case AtomicExpr::AO__atomic_load:
492 case AtomicExpr::AO__scoped_atomic_load_n:
493 case AtomicExpr::AO__scoped_atomic_load: {
494 cir::LoadOp load =
495 builder.createLoad(loc, ptr, /*isVolatile=*/expr->isVolatile());
496
497 load->setAttr("mem_order", orderAttr);
498 load->setAttr("sync_scope", scopeAttr);
499
500 builder.createStore(loc, load->getResult(0), dest);
501 return;
502 }
503
504 case AtomicExpr::AO__c11_atomic_store:
505 case AtomicExpr::AO__atomic_store_n:
506 case AtomicExpr::AO__atomic_store:
507 case AtomicExpr::AO__scoped_atomic_store:
508 case AtomicExpr::AO__scoped_atomic_store_n: {
509 cir::LoadOp loadVal1 = builder.createLoad(loc, val1);
510
512
513 builder.createStore(loc, loadVal1, ptr, expr->isVolatile(),
514 /*align=*/mlir::IntegerAttr{}, scopeAttr, orderAttr);
515 return;
516 }
517
518 case AtomicExpr::AO__c11_atomic_exchange:
519 case AtomicExpr::AO__atomic_exchange_n:
520 case AtomicExpr::AO__atomic_exchange:
521 case AtomicExpr::AO__scoped_atomic_exchange_n:
522 case AtomicExpr::AO__scoped_atomic_exchange:
523 opName = cir::AtomicXchgOp::getOperationName();
524 break;
525
526 case AtomicExpr::AO__atomic_add_fetch:
527 case AtomicExpr::AO__scoped_atomic_add_fetch:
528 fetchFirst = false;
529 [[fallthrough]];
530 case AtomicExpr::AO__c11_atomic_fetch_add:
531 case AtomicExpr::AO__atomic_fetch_add:
532 case AtomicExpr::AO__scoped_atomic_fetch_add:
533 opName = cir::AtomicFetchOp::getOperationName();
534 fetchAttr = cir::AtomicFetchKindAttr::get(builder.getContext(),
535 cir::AtomicFetchKind::Add);
536 break;
537
538 case AtomicExpr::AO__atomic_sub_fetch:
539 case AtomicExpr::AO__scoped_atomic_sub_fetch:
540 fetchFirst = false;
541 [[fallthrough]];
542 case AtomicExpr::AO__c11_atomic_fetch_sub:
543 case AtomicExpr::AO__atomic_fetch_sub:
544 case AtomicExpr::AO__scoped_atomic_fetch_sub:
545 opName = cir::AtomicFetchOp::getOperationName();
546 fetchAttr = cir::AtomicFetchKindAttr::get(builder.getContext(),
547 cir::AtomicFetchKind::Sub);
548 break;
549
550 case AtomicExpr::AO__atomic_min_fetch:
551 case AtomicExpr::AO__scoped_atomic_min_fetch:
552 fetchFirst = false;
553 [[fallthrough]];
554 case AtomicExpr::AO__c11_atomic_fetch_min:
555 case AtomicExpr::AO__atomic_fetch_min:
556 case AtomicExpr::AO__scoped_atomic_fetch_min:
557 opName = cir::AtomicFetchOp::getOperationName();
558 fetchAttr = cir::AtomicFetchKindAttr::get(builder.getContext(),
559 cir::AtomicFetchKind::Min);
560 break;
561
562 case AtomicExpr::AO__atomic_max_fetch:
563 case AtomicExpr::AO__scoped_atomic_max_fetch:
564 fetchFirst = false;
565 [[fallthrough]];
566 case AtomicExpr::AO__c11_atomic_fetch_max:
567 case AtomicExpr::AO__atomic_fetch_max:
568 case AtomicExpr::AO__scoped_atomic_fetch_max:
569 opName = cir::AtomicFetchOp::getOperationName();
570 fetchAttr = cir::AtomicFetchKindAttr::get(builder.getContext(),
571 cir::AtomicFetchKind::Max);
572 break;
573
574 case AtomicExpr::AO__atomic_and_fetch:
575 case AtomicExpr::AO__scoped_atomic_and_fetch:
576 fetchFirst = false;
577 [[fallthrough]];
578 case AtomicExpr::AO__c11_atomic_fetch_and:
579 case AtomicExpr::AO__atomic_fetch_and:
580 case AtomicExpr::AO__scoped_atomic_fetch_and:
581 opName = cir::AtomicFetchOp::getOperationName();
582 fetchAttr = cir::AtomicFetchKindAttr::get(builder.getContext(),
583 cir::AtomicFetchKind::And);
584 break;
585
586 case AtomicExpr::AO__atomic_or_fetch:
587 case AtomicExpr::AO__scoped_atomic_or_fetch:
588 fetchFirst = false;
589 [[fallthrough]];
590 case AtomicExpr::AO__c11_atomic_fetch_or:
591 case AtomicExpr::AO__atomic_fetch_or:
592 case AtomicExpr::AO__scoped_atomic_fetch_or:
593 opName = cir::AtomicFetchOp::getOperationName();
594 fetchAttr = cir::AtomicFetchKindAttr::get(builder.getContext(),
595 cir::AtomicFetchKind::Or);
596 break;
597
598 case AtomicExpr::AO__atomic_xor_fetch:
599 case AtomicExpr::AO__scoped_atomic_xor_fetch:
600 fetchFirst = false;
601 [[fallthrough]];
602 case AtomicExpr::AO__c11_atomic_fetch_xor:
603 case AtomicExpr::AO__atomic_fetch_xor:
604 case AtomicExpr::AO__scoped_atomic_fetch_xor:
605 opName = cir::AtomicFetchOp::getOperationName();
606 fetchAttr = cir::AtomicFetchKindAttr::get(builder.getContext(),
607 cir::AtomicFetchKind::Xor);
608 break;
609
610 case AtomicExpr::AO__atomic_nand_fetch:
611 case AtomicExpr::AO__scoped_atomic_nand_fetch:
612 fetchFirst = false;
613 [[fallthrough]];
614 case AtomicExpr::AO__c11_atomic_fetch_nand:
615 case AtomicExpr::AO__atomic_fetch_nand:
616 case AtomicExpr::AO__scoped_atomic_fetch_nand:
617 opName = cir::AtomicFetchOp::getOperationName();
618 fetchAttr = cir::AtomicFetchKindAttr::get(builder.getContext(),
619 cir::AtomicFetchKind::Nand);
620 break;
621
622 case AtomicExpr::AO__atomic_test_and_set: {
623 auto op = cir::AtomicTestAndSetOp::create(
624 builder, loc, ptr.getPointer(), order,
625 builder.getI64IntegerAttr(ptr.getAlignment().getQuantity()),
626 expr->isVolatile());
627 builder.createStore(loc, op, dest);
628 return;
629 }
630
631 case AtomicExpr::AO__atomic_clear: {
632 cir::AtomicClearOp::create(
633 builder, loc, ptr.getPointer(), order,
634 builder.getI64IntegerAttr(ptr.getAlignment().getQuantity()),
635 expr->isVolatile());
636 return;
637 }
638
639 case AtomicExpr::AO__atomic_fetch_uinc:
640 case AtomicExpr::AO__scoped_atomic_fetch_uinc:
641 opName = cir::AtomicFetchOp::getOperationName();
642 fetchAttr = cir::AtomicFetchKindAttr::get(builder.getContext(),
643 cir::AtomicFetchKind::UIncWrap);
644 break;
645
646 case AtomicExpr::AO__atomic_fetch_udec:
647 case AtomicExpr::AO__scoped_atomic_fetch_udec:
648 opName = cir::AtomicFetchOp::getOperationName();
649 fetchAttr = cir::AtomicFetchKindAttr::get(builder.getContext(),
650 cir::AtomicFetchKind::UDecWrap);
651 break;
652
653 case AtomicExpr::AO__opencl_atomic_init:
654
655 case AtomicExpr::AO__hip_atomic_compare_exchange_strong:
656 case AtomicExpr::AO__opencl_atomic_compare_exchange_strong:
657
658 case AtomicExpr::AO__opencl_atomic_compare_exchange_weak:
659 case AtomicExpr::AO__hip_atomic_compare_exchange_weak:
660
661 case AtomicExpr::AO__opencl_atomic_load:
662 case AtomicExpr::AO__hip_atomic_load:
663
664 case AtomicExpr::AO__opencl_atomic_store:
665 case AtomicExpr::AO__hip_atomic_store:
666
667 case AtomicExpr::AO__hip_atomic_exchange:
668 case AtomicExpr::AO__opencl_atomic_exchange:
669
670 case AtomicExpr::AO__hip_atomic_fetch_add:
671 case AtomicExpr::AO__opencl_atomic_fetch_add:
672
673 case AtomicExpr::AO__hip_atomic_fetch_sub:
674 case AtomicExpr::AO__opencl_atomic_fetch_sub:
675
676 case AtomicExpr::AO__hip_atomic_fetch_min:
677 case AtomicExpr::AO__opencl_atomic_fetch_min:
678
679 case AtomicExpr::AO__hip_atomic_fetch_max:
680 case AtomicExpr::AO__opencl_atomic_fetch_max:
681
682 case AtomicExpr::AO__hip_atomic_fetch_and:
683 case AtomicExpr::AO__opencl_atomic_fetch_and:
684
685 case AtomicExpr::AO__hip_atomic_fetch_or:
686 case AtomicExpr::AO__opencl_atomic_fetch_or:
687
688 case AtomicExpr::AO__hip_atomic_fetch_xor:
689 case AtomicExpr::AO__opencl_atomic_fetch_xor:
690 cgf.cgm.errorNYI(expr->getSourceRange(), "emitAtomicOp: expr op NYI");
691 return;
692 }
693
694 assert(!opName.empty() && "expected operation name to build");
695 mlir::Value loadVal1 = builder.createLoad(loc, val1);
696
697 SmallVector<mlir::Value> atomicOperands = {ptr.getPointer(), loadVal1};
698 SmallVector<mlir::Type> atomicResTys = {loadVal1.getType()};
699 mlir::Operation *rmwOp = builder.create(loc, builder.getStringAttr(opName),
700 atomicOperands, atomicResTys);
701
702 if (fetchAttr)
703 rmwOp->setAttr("binop", fetchAttr);
704 rmwOp->setAttr("mem_order", orderAttr);
705 rmwOp->setAttr("sync_scope", scopeAttr);
706 if (expr->isVolatile())
707 rmwOp->setAttr("is_volatile", builder.getUnitAttr());
708 if (fetchFirst && opName == cir::AtomicFetchOp::getOperationName())
709 rmwOp->setAttr("fetch_first", builder.getUnitAttr());
710
711 mlir::Value result = rmwOp->getResult(0);
712 builder.createStore(loc, result, dest);
713}
714
715// Map clang sync scope to CIR sync scope.
716static cir::SyncScopeKind convertSyncScopeToCIR(CIRGenFunction &cgf,
717 SourceRange range,
718 clang::SyncScope scope) {
719 switch (scope) {
721 return cir::SyncScopeKind::SingleThread;
723 return cir::SyncScopeKind::System;
725 return cir::SyncScopeKind::Device;
727 return cir::SyncScopeKind::Workgroup;
729 return cir::SyncScopeKind::Wavefront;
731 return cir::SyncScopeKind::Cluster;
732
734 return cir::SyncScopeKind::HIPSingleThread;
736 return cir::SyncScopeKind::HIPSystem;
738 return cir::SyncScopeKind::HIPAgent;
740 return cir::SyncScopeKind::HIPWorkgroup;
742 return cir::SyncScopeKind::HIPWavefront;
744 return cir::SyncScopeKind::HIPCluster;
745
747 return cir::SyncScopeKind::OpenCLWorkGroup;
749 return cir::SyncScopeKind::OpenCLDevice;
751 return cir::SyncScopeKind::OpenCLAllSVMDevices;
753 return cir::SyncScopeKind::OpenCLSubGroup;
754 }
755
756 llvm_unreachable("unhandled sync scope");
757}
758
760 Address ptr, Address val1, Address val2,
761 Expr *isWeakExpr, Expr *failureOrderExpr, int64_t size,
762 cir::MemOrder order,
763 const std::optional<Expr::EvalResult> &scopeConst,
764 mlir::Value scopeValue) {
765 std::unique_ptr<AtomicScopeModel> scopeModel = expr->getScopeModel();
766
767 if (!scopeModel) {
768 emitAtomicOp(cgf, expr, dest, ptr, val1, val2, isWeakExpr, failureOrderExpr,
769 size, order, cir::SyncScopeKind::System);
770 return;
771 }
772
773 if (scopeConst.has_value()) {
774 cir::SyncScopeKind mappedScope = convertSyncScopeToCIR(
775 cgf, expr->getScope()->getSourceRange(),
776 scopeModel->map(scopeConst->Val.getInt().getZExtValue()));
777 emitAtomicOp(cgf, expr, dest, ptr, val1, val2, isWeakExpr, failureOrderExpr,
778 size, order, mappedScope);
779 return;
780 }
781
782 // The sync scope is not a compile-time constant. Emit a switch statement to
783 // handle each possible value of the sync scope.
784 CIRGenBuilderTy &builder = cgf.getBuilder();
785 mlir::Location loc = cgf.getLoc(expr->getSourceRange());
786 llvm::ArrayRef<unsigned> allScopes = scopeModel->getRuntimeValues();
787 unsigned fallback = scopeModel->getFallBackValue();
788
789 cir::SwitchOp::create(
790 builder, loc, scopeValue,
791 [&](mlir::OpBuilder &, mlir::Location loc, mlir::OperationState &) {
792 mlir::Block *switchBlock = builder.getBlock();
793
794 // Default case -- use fallback scope
795 cir::SyncScopeKind fallbackScope = convertSyncScopeToCIR(
796 cgf, expr->getScope()->getSourceRange(), scopeModel->map(fallback));
797 emitDefaultCaseLabel(builder, loc);
798 emitAtomicOp(cgf, expr, dest, ptr, val1, val2, isWeakExpr,
799 failureOrderExpr, size, order, fallbackScope);
800 builder.createBreak(loc);
801 builder.setInsertionPointToEnd(switchBlock);
802
803 // Emit a switch case for each non-fallback runtime scope value
804 for (unsigned scope : allScopes) {
805 if (scope == fallback)
806 continue;
807
808 cir::SyncScopeKind cirScope = convertSyncScopeToCIR(
809 cgf, expr->getScope()->getSourceRange(), scopeModel->map(scope));
810
811 mlir::ArrayAttr casesAttr = builder.getArrayAttr(
812 {cir::IntAttr::get(scopeValue.getType(), scope)});
813 mlir::OpBuilder::InsertPoint insertPoint;
814 cir::CaseOp::create(builder, loc, casesAttr, cir::CaseOpKind::Equal,
815 insertPoint);
816
817 builder.restoreInsertionPoint(insertPoint);
818 emitAtomicOp(cgf, expr, dest, ptr, val1, val2, isWeakExpr,
819 failureOrderExpr, size, order, cirScope);
820 builder.createBreak(loc);
821 builder.setInsertionPointToEnd(switchBlock);
822 }
823
824 builder.createYield(loc);
825 });
826}
827
828static std::optional<cir::MemOrder>
829getEffectiveAtomicMemOrder(cir::MemOrder oriOrder, bool isStore, bool isLoad,
830 bool isFence) {
831 // Some memory orders are not supported by partial atomic operation:
832 // {memory_order_releaxed} is not valid for fence operations.
833 // {memory_order_consume, memory_order_acquire} are not valid for write-only
834 // operations.
835 // {memory_order_release} is not valid for read-only operations.
836 // {memory_order_acq_rel} is only valid for read-write operations.
837 if (isStore) {
838 if (oriOrder == cir::MemOrder::Consume ||
839 oriOrder == cir::MemOrder::Acquire ||
840 oriOrder == cir::MemOrder::AcquireRelease)
841 return std::nullopt;
842 } else if (isLoad) {
843 if (oriOrder == cir::MemOrder::Release ||
844 oriOrder == cir::MemOrder::AcquireRelease)
845 return std::nullopt;
846 } else if (isFence) {
847 if (oriOrder == cir::MemOrder::Relaxed)
848 return std::nullopt;
849 }
850 // memory_order_consume is not implemented, it is always treated like
851 // memory_order_acquire
852 if (oriOrder == cir::MemOrder::Consume)
853 return cir::MemOrder::Acquire;
854 return oriOrder;
855}
856
858 CIRGenFunction &cgf, mlir::Value order, bool isStore, bool isLoad,
859 bool isFence, llvm::function_ref<void(cir::MemOrder)> emitAtomicOpFn) {
860 if (!order)
861 return;
862 // The memory order is not known at compile-time. The atomic operations
863 // can't handle runtime memory orders; the memory order must be hard coded.
864 // Generate a "switch" statement that converts a runtime value into a
865 // compile-time value.
866 CIRGenBuilderTy &builder = cgf.getBuilder();
867 cir::SwitchOp::create(
868 builder, order.getLoc(), order,
869 [&](mlir::OpBuilder &, mlir::Location loc, mlir::OperationState &) {
870 mlir::Block *switchBlock = builder.getBlock();
871
872 auto emitMemOrderCase = [&](llvm::ArrayRef<cir::MemOrder> caseOrders) {
873 // Checking there are same effective memory order for each case.
874 for (int i = 1, e = caseOrders.size(); i < e; i++)
875 assert((getEffectiveAtomicMemOrder(caseOrders[i - 1], isStore,
876 isLoad, isFence) ==
877 getEffectiveAtomicMemOrder(caseOrders[i], isStore, isLoad,
878 isFence)) &&
879 "Effective memory order must be same!");
880 // Emit case label and atomic opeartion if neccessary.
881 if (caseOrders.empty()) {
882 emitDefaultCaseLabel(builder, loc);
883 // There is no good way to report an unsupported memory order at
884 // runtime, hence the fallback to memory_order_relaxed.
885 if (!isFence)
886 emitAtomicOpFn(cir::MemOrder::Relaxed);
887 } else if (std::optional<cir::MemOrder> actualOrder =
888 getEffectiveAtomicMemOrder(caseOrders[0], isStore,
889 isLoad, isFence)) {
890 // Included in default case.
891 if (!isFence && actualOrder == cir::MemOrder::Relaxed)
892 return;
893 // Creating case operation for effective memory order. If there are
894 // multiple cases in `caseOrders`, the actual order of each case
895 // must be same, this needs to be guaranteed by the caller.
896 emitMemOrderCaseLabel(builder, loc, order.getType(), caseOrders);
897 emitAtomicOpFn(actualOrder.value());
898 } else {
899 // Do nothing if (!caseOrders.empty() && !actualOrder)
900 return;
901 }
902 builder.createBreak(loc);
903 builder.setInsertionPointToEnd(switchBlock);
904 };
905
906 emitMemOrderCase(/*default:*/ {});
907 emitMemOrderCase({cir::MemOrder::Relaxed});
908 emitMemOrderCase({cir::MemOrder::Consume, cir::MemOrder::Acquire});
909 emitMemOrderCase({cir::MemOrder::Release});
910 emitMemOrderCase({cir::MemOrder::AcquireRelease});
911 emitMemOrderCase({cir::MemOrder::SequentiallyConsistent});
912
913 builder.createYield(loc);
914 });
915}
916
918 const Expr *memOrder, bool isStore, bool isLoad, bool isFence,
919 llvm::function_ref<void(cir::MemOrder)> emitAtomicOpFn) {
920 // Emit the memory order operand, and try to evaluate it as a constant.
921 Expr::EvalResult eval;
922 if (memOrder->EvaluateAsInt(eval, getContext())) {
923 uint64_t constOrder = eval.Val.getInt().getZExtValue();
924 // We should not ever get to a case where the ordering isn't a valid CABI
925 // value, but it's hard to enforce that in general.
926 if (!cir::isValidCIRAtomicOrderingCABI(constOrder))
927 return;
928 cir::MemOrder oriOrder = static_cast<cir::MemOrder>(constOrder);
929 if (std::optional<cir::MemOrder> actualOrder =
930 getEffectiveAtomicMemOrder(oriOrder, isStore, isLoad, isFence))
931 emitAtomicOpFn(actualOrder.value());
932 return;
933 }
934
935 // Otherwise, handle variable memory ordering. Emit `SwitchOp` to convert
936 // dynamic value to static value.
937 mlir::Value dynOrder = emitScalarExpr(memOrder);
938 emitAtomicExprWithDynamicMemOrder(*this, dynOrder, isStore, isLoad, isFence,
939 emitAtomicOpFn);
940}
941
943 QualType atomicTy = e->getPtr()->getType()->getPointeeType();
944 QualType memTy = atomicTy;
945 if (const auto *ty = atomicTy->getAs<AtomicType>())
946 memTy = ty->getValueType();
947
948 Expr *isWeakExpr = nullptr;
949 Expr *orderFailExpr = nullptr;
950
951 Address val1 = Address::invalid();
952 Address val2 = Address::invalid();
953 Address dest = Address::invalid();
955
957 if (e->getOp() == AtomicExpr::AO__c11_atomic_init) {
958 LValue lvalue = makeAddrLValue(ptr, atomicTy);
959 emitAtomicInit(e->getVal1(), lvalue);
960 return RValue::get(nullptr);
961 }
962
963 TypeInfoChars typeInfo = getContext().getTypeInfoInChars(atomicTy);
964 uint64_t size = typeInfo.Width.getQuantity();
965
966 // Emit the sync scope operand, and try to evaluate it as a constant.
967 mlir::Value scope =
968 e->getScopeModel() ? emitScalarExpr(e->getScope()) : nullptr;
969 std::optional<Expr::EvalResult> scopeConst;
970 if (Expr::EvalResult eval;
971 e->getScopeModel() && e->getScope()->EvaluateAsInt(eval, getContext()))
972 scopeConst.emplace(std::move(eval));
973
974 switch (e->getOp()) {
975 default:
976 cgm.errorNYI(e->getSourceRange(), "atomic op NYI");
977 return RValue::get(nullptr);
978
979 case AtomicExpr::AO__c11_atomic_init:
980 llvm_unreachable("already handled above with emitAtomicInit");
981
982 case AtomicExpr::AO__atomic_load_n:
983 case AtomicExpr::AO__scoped_atomic_load_n:
984 case AtomicExpr::AO__c11_atomic_load:
985 case AtomicExpr::AO__atomic_test_and_set:
986 case AtomicExpr::AO__atomic_clear:
987 break;
988
989 case AtomicExpr::AO__atomic_load:
990 case AtomicExpr::AO__scoped_atomic_load:
992 break;
993
994 case AtomicExpr::AO__atomic_store:
995 case AtomicExpr::AO__scoped_atomic_store:
997 break;
998
999 case AtomicExpr::AO__atomic_exchange:
1000 case AtomicExpr::AO__scoped_atomic_exchange:
1001 val1 = emitPointerWithAlignment(e->getVal1());
1002 dest = emitPointerWithAlignment(e->getVal2());
1003 break;
1004
1005 case AtomicExpr::AO__atomic_compare_exchange:
1006 case AtomicExpr::AO__atomic_compare_exchange_n:
1007 case AtomicExpr::AO__c11_atomic_compare_exchange_weak:
1008 case AtomicExpr::AO__c11_atomic_compare_exchange_strong:
1009 case AtomicExpr::AO__scoped_atomic_compare_exchange:
1010 case AtomicExpr::AO__scoped_atomic_compare_exchange_n:
1011 val1 = emitPointerWithAlignment(e->getVal1());
1012 if (e->getOp() == AtomicExpr::AO__atomic_compare_exchange ||
1013 e->getOp() == AtomicExpr::AO__scoped_atomic_compare_exchange)
1014 val2 = emitPointerWithAlignment(e->getVal2());
1015 else
1016 val2 = emitValToTemp(*this, e->getVal2());
1017 orderFailExpr = e->getOrderFail();
1018 if (e->getOp() == AtomicExpr::AO__atomic_compare_exchange_n ||
1019 e->getOp() == AtomicExpr::AO__atomic_compare_exchange ||
1020 e->getOp() == AtomicExpr::AO__scoped_atomic_compare_exchange_n ||
1021 e->getOp() == AtomicExpr::AO__scoped_atomic_compare_exchange)
1022 isWeakExpr = e->getWeak();
1023 break;
1024
1025 case AtomicExpr::AO__c11_atomic_fetch_add:
1026 case AtomicExpr::AO__c11_atomic_fetch_sub:
1027 if (memTy->isPointerType()) {
1028 cgm.errorNYI(e->getSourceRange(),
1029 "atomic fetch-and-add and fetch-and-sub for pointers");
1030 return RValue::get(nullptr);
1031 }
1032 [[fallthrough]];
1033 case AtomicExpr::AO__atomic_fetch_add:
1034 case AtomicExpr::AO__atomic_fetch_max:
1035 case AtomicExpr::AO__atomic_fetch_min:
1036 case AtomicExpr::AO__atomic_fetch_sub:
1037 case AtomicExpr::AO__atomic_add_fetch:
1038 case AtomicExpr::AO__atomic_max_fetch:
1039 case AtomicExpr::AO__atomic_min_fetch:
1040 case AtomicExpr::AO__atomic_sub_fetch:
1041 case AtomicExpr::AO__c11_atomic_fetch_max:
1042 case AtomicExpr::AO__c11_atomic_fetch_min:
1043 case AtomicExpr::AO__scoped_atomic_fetch_add:
1044 case AtomicExpr::AO__scoped_atomic_fetch_max:
1045 case AtomicExpr::AO__scoped_atomic_fetch_min:
1046 case AtomicExpr::AO__scoped_atomic_fetch_sub:
1047 case AtomicExpr::AO__scoped_atomic_add_fetch:
1048 case AtomicExpr::AO__scoped_atomic_max_fetch:
1049 case AtomicExpr::AO__scoped_atomic_min_fetch:
1050 case AtomicExpr::AO__scoped_atomic_sub_fetch:
1051 [[fallthrough]];
1052
1053 case AtomicExpr::AO__atomic_fetch_and:
1054 case AtomicExpr::AO__atomic_fetch_nand:
1055 case AtomicExpr::AO__atomic_fetch_or:
1056 case AtomicExpr::AO__atomic_fetch_xor:
1057 case AtomicExpr::AO__atomic_and_fetch:
1058 case AtomicExpr::AO__atomic_nand_fetch:
1059 case AtomicExpr::AO__atomic_or_fetch:
1060 case AtomicExpr::AO__atomic_xor_fetch:
1061 case AtomicExpr::AO__atomic_exchange_n:
1062 case AtomicExpr::AO__atomic_store_n:
1063 case AtomicExpr::AO__c11_atomic_fetch_and:
1064 case AtomicExpr::AO__c11_atomic_fetch_nand:
1065 case AtomicExpr::AO__c11_atomic_fetch_or:
1066 case AtomicExpr::AO__c11_atomic_fetch_xor:
1067 case AtomicExpr::AO__c11_atomic_exchange:
1068 case AtomicExpr::AO__c11_atomic_store:
1069 case AtomicExpr::AO__scoped_atomic_fetch_and:
1070 case AtomicExpr::AO__scoped_atomic_fetch_nand:
1071 case AtomicExpr::AO__scoped_atomic_fetch_or:
1072 case AtomicExpr::AO__scoped_atomic_fetch_xor:
1073 case AtomicExpr::AO__scoped_atomic_and_fetch:
1074 case AtomicExpr::AO__scoped_atomic_nand_fetch:
1075 case AtomicExpr::AO__scoped_atomic_or_fetch:
1076 case AtomicExpr::AO__scoped_atomic_xor_fetch:
1077 case AtomicExpr::AO__scoped_atomic_store_n:
1078 case AtomicExpr::AO__scoped_atomic_exchange_n:
1079 case AtomicExpr::AO__atomic_fetch_uinc:
1080 case AtomicExpr::AO__atomic_fetch_udec:
1081 case AtomicExpr::AO__scoped_atomic_fetch_uinc:
1082 case AtomicExpr::AO__scoped_atomic_fetch_udec:
1083 val1 = emitValToTemp(*this, e->getVal1());
1084 break;
1085 }
1086
1087 QualType resultTy = e->getType().getUnqualifiedType();
1088
1089 bool shouldCastToIntPtrTy =
1091
1092 // The inlined atomics only function on iN types, where N is a power of 2. We
1093 // need to make sure (via temporaries if necessary) that all incoming values
1094 // are compatible.
1095 LValue atomicValue = makeAddrLValue(ptr, atomicTy);
1096 AtomicInfo atomics(*this, atomicValue, getLoc(e->getSourceRange()));
1097
1098 if (shouldCastToIntPtrTy) {
1099 ptr = atomics.castToAtomicIntPointer(ptr);
1100 if (val1.isValid())
1101 val1 = atomics.convertToAtomicIntPointer(val1);
1102 }
1103 if (dest.isValid()) {
1104 if (shouldCastToIntPtrTy)
1105 dest = atomics.castToAtomicIntPointer(dest);
1106 } else if (e->isCmpXChg()) {
1107 dest = createMemTemp(resultTy, getLoc(e->getSourceRange()), "cmpxchg.bool");
1108 } else if (e->getOp() == AtomicExpr::AO__atomic_test_and_set) {
1109 dest = createMemTemp(resultTy, getLoc(e->getSourceRange()),
1110 "test_and_set.bool");
1111 } else if (!resultTy->isVoidType()) {
1112 dest = atomics.createTempAlloca();
1113 if (shouldCastToIntPtrTy)
1114 dest = atomics.castToAtomicIntPointer(dest);
1115 }
1116
1117 bool powerOf2Size = (size & (size - 1)) == 0;
1118 bool useLibCall = !powerOf2Size || (size > 16);
1119
1120 // For atomics larger than 16 bytes, emit a libcall from the frontend. This
1121 // avoids the overhead of dealing with excessively-large value types in IR.
1122 // Non-power-of-2 values also lower to libcall here, as they are not currently
1123 // permitted in IR instructions (although that constraint could be relaxed in
1124 // the future). For other cases where a libcall is required on a given
1125 // platform, we let the backend handle it (this includes handling for all of
1126 // the size-optimized libcall variants, which are only valid up to 16 bytes.)
1127 //
1128 // See: https://llvm.org/docs/Atomics.html#libcalls-atomic
1129 if (useLibCall) {
1131 cgm.errorNYI(e->getSourceRange(), "emitAtomicExpr: emit atomic lib call");
1132 return RValue::get(nullptr);
1133 }
1134
1135 bool isStore = e->getOp() == AtomicExpr::AO__c11_atomic_store ||
1136 e->getOp() == AtomicExpr::AO__opencl_atomic_store ||
1137 e->getOp() == AtomicExpr::AO__hip_atomic_store ||
1138 e->getOp() == AtomicExpr::AO__atomic_store ||
1139 e->getOp() == AtomicExpr::AO__atomic_store_n ||
1140 e->getOp() == AtomicExpr::AO__scoped_atomic_store ||
1141 e->getOp() == AtomicExpr::AO__scoped_atomic_store_n ||
1142 e->getOp() == AtomicExpr::AO__atomic_clear;
1143 bool isLoad = e->getOp() == AtomicExpr::AO__c11_atomic_load ||
1144 e->getOp() == AtomicExpr::AO__opencl_atomic_load ||
1145 e->getOp() == AtomicExpr::AO__hip_atomic_load ||
1146 e->getOp() == AtomicExpr::AO__atomic_load ||
1147 e->getOp() == AtomicExpr::AO__atomic_load_n ||
1148 e->getOp() == AtomicExpr::AO__scoped_atomic_load ||
1149 e->getOp() == AtomicExpr::AO__scoped_atomic_load_n;
1150
1151 auto emitAtomicOpCallBackFn = [&](cir::MemOrder memOrder) {
1152 emitAtomicOp(*this, e, dest, ptr, val1, val2, isWeakExpr, orderFailExpr,
1153 size, memOrder, scopeConst, scope);
1154 };
1155 emitAtomicExprWithMemOrder(e->getOrder(), isStore, isLoad, /*isFence*/ false,
1156 emitAtomicOpCallBackFn);
1157
1158 if (resultTy->isVoidType())
1159 return RValue::get(nullptr);
1160
1161 return convertTempToRValue(
1162 dest.withElementType(builder, convertTypeForMem(resultTy)), resultTy,
1163 e->getExprLoc());
1164}
1165
1166void CIRGenFunction::emitAtomicStore(RValue rvalue, LValue dest, bool isInit) {
1167 bool isVolatile = dest.isVolatileQualified();
1168 auto order = cir::MemOrder::SequentiallyConsistent;
1169 if (!dest.getType()->isAtomicType()) {
1171 }
1172 return emitAtomicStore(rvalue, dest, order, isVolatile, isInit);
1173}
1174
1175/// Emit a store to an l-value of atomic type.
1176///
1177/// Note that the r-value is expected to be an r-value of the atomic type; this
1178/// means that for aggregate r-values, it should include storage for any padding
1179/// that was necessary.
1181 cir::MemOrder order, bool isVolatile,
1182 bool isInit) {
1183 // If this is an aggregate r-value, it should agree in type except
1184 // maybe for address-space qualification.
1185 mlir::Location loc = dest.getPointer().getLoc();
1186 assert(!rvalue.isAggregate() ||
1188 dest.getAddress().getElementType());
1189
1190 AtomicInfo atomics(*this, dest, loc);
1191 LValue lvalue = atomics.getAtomicLValue();
1192
1193 if (lvalue.isSimple()) {
1194 // If this is an initialization, just put the value there normally.
1195 if (isInit) {
1196 atomics.emitCopyIntoMemory(rvalue);
1197 return;
1198 }
1199
1200 // Check whether we should use a library call.
1201 if (atomics.shouldUseLibCall()) {
1203 cgm.errorNYI(loc, "emitAtomicStore: atomic store with library call");
1204 return;
1205 }
1206
1207 // Okay, we're doing this natively.
1208 mlir::Value valueToStore = atomics.convertRValueToInt(rvalue);
1209
1210 // Do the atomic store.
1211 Address addr = atomics.getAtomicAddress();
1212 if (mlir::Value value = atomics.getScalarRValValueOrNull(rvalue)) {
1213 if (shouldCastToInt(value.getType(), /*CmpXchg=*/false)) {
1214 addr = atomics.castToAtomicIntPointer(addr);
1215 valueToStore =
1216 builder.createIntCast(valueToStore, addr.getElementType());
1217 }
1218 }
1219 cir::StoreOp store = builder.createStore(loc, valueToStore, addr);
1220
1221 // Initializations don't need to be atomic.
1222 if (!isInit) {
1224 store.setMemOrder(order);
1225 }
1226
1227 // Other decoration.
1228 if (isVolatile)
1229 store.setIsVolatile(true);
1230
1232 return;
1233 }
1234
1235 cgm.errorNYI(loc, "emitAtomicStore: non-simple atomic lvalue");
1237}
1238
1240 AtomicInfo atomics(*this, dest, getLoc(init->getSourceRange()));
1241
1242 switch (atomics.getEvaluationKind()) {
1243 case cir::TEK_Scalar: {
1244 mlir::Value value = emitScalarExpr(init);
1245 atomics.emitCopyIntoMemory(RValue::get(value));
1246 return;
1247 }
1248
1249 case cir::TEK_Complex: {
1250 mlir::Value value = emitComplexExpr(init);
1251 atomics.emitCopyIntoMemory(RValue::get(value));
1252 return;
1253 }
1254
1255 case cir::TEK_Aggregate: {
1256 // Fix up the destination if the initializer isn't an expression
1257 // of atomic type.
1258 bool zeroed = false;
1259 if (!init->getType()->isAtomicType()) {
1260 zeroed = atomics.emitMemSetZeroIfNecessary();
1261 dest = atomics.projectValue();
1262 }
1263
1264 // Evaluate the expression directly into the destination.
1270
1271 emitAggExpr(init, slot);
1272 return;
1273 }
1274 }
1275
1276 llvm_unreachable("bad evaluation kind");
1277}
static bool shouldCastToInt(mlir::Type valueTy, bool cmpxchg)
Return true if.
static Address emitValToTemp(CIRGenFunction &cgf, Expr *e)
static void emitAtomicCmpXchg(CIRGenFunction &cgf, AtomicExpr *e, bool isWeak, Address dest, Address ptr, Address val1, Address val2, uint64_t size, cir::MemOrder successOrder, cir::MemOrder failureOrder, cir::SyncScopeKind scope)
static void emitMemOrderCaseLabel(CIRGenBuilderTy &builder, mlir::Location loc, mlir::Type orderType, llvm::ArrayRef< cir::MemOrder > orders)
static cir::SyncScopeKind convertSyncScopeToCIR(CIRGenFunction &cgf, SourceRange range, clang::SyncScope scope)
static void emitAtomicExprWithDynamicMemOrder(CIRGenFunction &cgf, mlir::Value order, bool isStore, bool isLoad, bool isFence, llvm::function_ref< void(cir::MemOrder)> emitAtomicOpFn)
static void emitAtomicOp(CIRGenFunction &cgf, AtomicExpr *expr, Address dest, Address ptr, Address val1, Address val2, Expr *isWeakExpr, Expr *failureOrderExpr, int64_t size, cir::MemOrder order, cir::SyncScopeKind scope)
static void emitDefaultCaseLabel(CIRGenBuilderTy &builder, mlir::Location loc)
static bool isFullSizeType(CIRGenModule &cgm, mlir::Type ty, uint64_t expectedSize)
Does a store of the given IR type modify the full expected width?
static std::optional< cir::MemOrder > getEffectiveAtomicMemOrder(cir::MemOrder oriOrder, bool isStore, bool isLoad, bool isFence)
static void emitAtomicCmpXchgFailureSet(CIRGenFunction &cgf, AtomicExpr *e, bool isWeak, Address dest, Address ptr, Address val1, Address val2, Expr *failureOrderExpr, uint64_t size, cir::MemOrder successOrder, cir::SyncScopeKind scope)
__device__ __2f16 b
cir::BreakOp createBreak(mlir::Location loc)
Create a break operation.
mlir::Value createPtrBitcast(mlir::Value src, mlir::Type newPointeeTy)
mlir::Value createNot(mlir::Location loc, mlir::Value value)
cir::YieldOp createYield(mlir::Location loc, mlir::ValueRange value={})
Create a yield operation.
cir::BoolType getBoolTy()
llvm::TypeSize getTypeSizeInBits(mlir::Type ty) const
llvm::TypeSize getTypeStoreSize(mlir::Type ty) const
Returns the maximum number of bytes that may be overwritten by storing the specified type.
APSInt & getInt()
Definition APValue.h:508
TypeInfo getTypeInfo(const Type *T) const
Get the size and alignment of the specified complete type in bits.
int64_t toBits(CharUnits CharSize) const
Convert a size in characters to a size in bits.
const TargetInfo & getTargetInfo() const
Definition ASTContext.h:917
CharUnits toCharUnitsFromBits(int64_t BitSize) const
Convert a size in bits to a size in characters.
AtomicExpr - Variadic atomic builtins: __atomic_exchange, __atomic_fetch_*, __atomic_load,...
Definition Expr.h:6927
static std::unique_ptr< AtomicScopeModel > getScopeModel(AtomicOp Op)
Get atomic scope model for the atomic op code.
Definition Expr.h:7076
Expr * getVal2() const
Definition Expr.h:6978
Expr * getOrder() const
Definition Expr.h:6961
Expr * getScope() const
Definition Expr.h:6964
bool isCmpXChg() const
Definition Expr.h:7011
AtomicOp getOp() const
Definition Expr.h:6990
Expr * getVal1() const
Definition Expr.h:6968
Expr * getPtr() const
Definition Expr.h:6958
Expr * getWeak() const
Definition Expr.h:6984
Expr * getOrderFail() const
Definition Expr.h:6974
bool isVolatile() const
Definition Expr.h:7007
Address withPointer(mlir::Value newPtr) const
Return address with different pointer, but same element type and alignment.
Definition Address.h:81
mlir::Value getPointer() const
Definition Address.h:96
mlir::Type getElementType() const
Definition Address.h:123
static Address invalid()
Definition Address.h:74
Address withElementType(CIRGenBuilderTy &builder, mlir::Type ElemTy) const
Return address with different element type, a bitcast pointer, and the same alignment.
clang::CharUnits getAlignment() const
Definition Address.h:136
bool isValid() const
Definition Address.h:75
An aggregate value slot.
static AggValueSlot forLValue(const LValue &LV, IsDestructed_t isDestructed, IsAliased_t isAliased, Overlap_t mayOverlap, IsZeroed_t isZeroed=IsNotZeroed)
cir::StoreOp createStore(mlir::Location loc, mlir::Value val, Address dst, bool isVolatile=false, mlir::IntegerAttr align={}, cir::SyncScopeKindAttr scope={}, cir::MemOrderAttr order={})
cir::LoadOp createLoad(mlir::Location loc, Address addr, bool isVolatile=false)
cir::IntType getUIntNTy(int n)
RValue convertTempToRValue(Address addr, clang::QualType type, clang::SourceLocation loc)
Given the address of a temporary variable, produce an r-value of its type.
Address emitPointerWithAlignment(const clang::Expr *expr, LValueBaseInfo *baseInfo=nullptr)
Given an expression with a pointer type, emit the value and compute our best estimate of the alignmen...
mlir::Value emitComplexExpr(const Expr *e)
Emit the computation of the specified expression of complex type, returning the result.
mlir::Location getLoc(clang::SourceLocation srcLoc)
Helpers to convert Clang's SourceLocation to a MLIR Location.
void emitAnyExprToMem(const Expr *e, Address location, Qualifiers quals, bool isInitializer)
Emits the code necessary to evaluate an arbitrary expression into the given memory location.
RValue emitAtomicExpr(AtomicExpr *e)
mlir::Type convertTypeForMem(QualType t)
void emitStoreOfScalar(mlir::Value value, Address addr, bool isVolatile, clang::QualType ty, LValueBaseInfo baseInfo, bool isInit=false, bool isNontemporal=false)
void emitAtomicExprWithMemOrder(const Expr *memOrder, bool isStore, bool isLoad, bool isFence, llvm::function_ref< void(cir::MemOrder)> emitAtomicOp)
mlir::Value emitToMemory(mlir::Value value, clang::QualType ty)
Given a value and its clang type, returns the value casted to its memory representation.
mlir::Value emitScalarExpr(const clang::Expr *e, bool ignoreResultAssign=false)
Emit the computation of the specified expression of scalar type.
CIRGenBuilderTy & getBuilder()
mlir::MLIRContext & getMLIRContext()
void emitAtomicInit(Expr *init, LValue dest)
LValue makeAddrLValue(Address addr, QualType ty, AlignmentSource source=AlignmentSource::Type)
void emitAtomicStore(RValue rvalue, LValue dest, bool isInit)
clang::ASTContext & getContext() const
Address createMemTemp(QualType t, mlir::Location loc, const Twine &name="tmp", Address *alloca=nullptr, mlir::OpBuilder::InsertPoint ip={})
Create a temporary memory object of the given type, with appropriate alignmen and cast it to the defa...
void emitAggExpr(const clang::Expr *e, AggValueSlot slot)
This class organizes the cross-function state that is used while generating CIR code.
DiagnosticBuilder errorNYI(SourceLocation, llvm::StringRef)
Helpers to emit "not yet implemented" error diagnostics.
const cir::CIRDataLayout getDataLayout() const
Address getAddress() const
clang::QualType getType() const
mlir::Value getPointer() const
bool isVolatileQualified() const
bool isSimple() const
This trivial value class is used to represent the result of an expression that is evaluated.
Definition CIRGenValue.h:33
Address getAggregateAddress() const
Return the value of the address of the aggregate.
Definition CIRGenValue.h:69
bool isAggregate() const
Definition CIRGenValue.h:51
static RValue get(mlir::Value v)
Definition CIRGenValue.h:83
mlir::Value getValue() const
Return the value of this scalar value.
Definition CIRGenValue.h:57
bool isScalar() const
Definition CIRGenValue.h:49
llvm::Align getAsAlign() const
getAsAlign - Returns Quantity as a valid llvm::Align, Beware llvm::Align assumes power of two 8-bit b...
Definition CharUnits.h:189
QuantityType getQuantity() const
getQuantity - Get the raw integer representation of this quantity.
Definition CharUnits.h:185
This represents one expression.
Definition Expr.h:112
bool EvaluateAsInt(EvalResult &Result, const ASTContext &Ctx, SideEffectsKind AllowSideEffects=SE_NoSideEffects, bool InConstantContext=false) const
EvaluateAsInt - Return true if this is a constant which we can fold and convert to an integer,...
bool EvaluateAsBooleanCondition(bool &Result, const ASTContext &Ctx, bool InConstantContext=false) const
EvaluateAsBooleanCondition - Return true if this is a constant which we can fold and convert to a boo...
SourceLocation getExprLoc() const LLVM_READONLY
getExprLoc - Return the preferred location for the arrow when diagnosing a problem with a generic exp...
Definition Expr.cpp:277
QualType getType() const
Definition Expr.h:144
A (possibly-)qualified type.
Definition TypeBase.h:937
Qualifiers getQualifiers() const
Retrieve the set of qualifiers applied to this type.
Definition TypeBase.h:8471
QualType getUnqualifiedType() const
Retrieve the unqualified variant of the given type, removing as little sugar as possible.
Definition TypeBase.h:8525
A trivial tuple used to represent a source range.
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
Definition Stmt.cpp:343
virtual bool hasBuiltinAtomic(uint64_t AtomicSizeInBits, uint64_t AlignmentInBits) const
Returns true if the given target supports lock-free atomic operations at the specified width and alig...
Definition TargetInfo.h:865
bool isVoidType() const
Definition TypeBase.h:9034
bool isPointerType() const
Definition TypeBase.h:8668
QualType getPointeeType() const
If this is a pointer, ObjC object pointer, or block pointer, this returns the respective pointee.
Definition Type.cpp:754
bool isAtomicType() const
Definition TypeBase.h:8860
const T * getAs() const
Member-template getAs<specific type>'.
Definition TypeBase.h:9261
bool isValidCIRAtomicOrderingCABI(Int value)
const internal::VariadicDynCastAllOfMatcher< Stmt, Expr > expr
Matches expressions.
@ Address
A pointer to a ValueDecl.
Definition Primitives.h:28
The JSON file list parser is used to communicate input to InstallAPI.
bool isa(CodeGen::Address addr)
Definition Address.h:330
SyncScope
Defines sync scope values used internally by clang.
Definition SyncScope.h:42
unsigned long uint64_t
static bool atomicInfoGetAtomicPointer()
static bool aggValueSlotGC()
static bool opLoadStoreAtomic()
static bool opLoadStoreTbaa()
static bool atomicUseLibCall()
static bool atomicOpenMP()
static bool atomicMicrosoftVolatile()
static bool atomicSyncScopeID()
static bool atomicInfoGetAtomicAddress()
EvalResult is a struct with detailed info about an evaluated expression.
Definition Expr.h:648
APValue Val
Val - This is the value the expression can be folded to.
Definition Expr.h:650