clang 24.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 QualType getAtomicType() const { return atomicTy; }
72 CharUnits getAtomicAlignment() const { return atomicAlign; }
73 TypeEvaluationKind getEvaluationKind() const { return evaluationKind; }
74 mlir::Value getAtomicPointer() const {
75 if (lvalue.isSimple())
76 return lvalue.getPointer();
78 return nullptr;
79 }
80 bool shouldUseLibCall() const { return useLibCall; }
81 const LValue &getAtomicLValue() const { return lvalue; }
82 Address getAtomicAddress() const {
83 mlir::Type elemTy;
84 if (lvalue.isSimple()) {
85 elemTy = lvalue.getAddress().getElementType();
86 } else {
88 cgf.cgm.errorNYI(loc, "AtomicInfo::getAtomicAddress: non-simple lvalue");
89 }
90 return Address(getAtomicPointer(), elemTy, getAtomicAlignment());
91 }
92
93 /// Is the atomic size larger than the underlying value type?
94 ///
95 /// Note that the absence of padding does not mean that atomic
96 /// objects are completely interchangeable with non-atomic
97 /// objects: we might have promoted the alignment of a type
98 /// without making it bigger.
99 bool hasPadding() const { return (valueSizeInBits != atomicSizeInBits); }
100
101 bool emitMemSetZeroIfNecessary() const;
102
103 mlir::Value getScalarRValValueOrNull(RValue rvalue) const;
104
105 /// Cast the given pointer to an integer pointer suitable for atomic
106 /// operations on the source.
107 Address castToAtomicIntPointer(Address addr) const;
108
109 /// If addr is compatible with the iN that will be used for an atomic
110 /// operation, bitcast it. Otherwise, create a temporary that is suitable and
111 /// copy the value across.
112 Address convertToAtomicIntPointer(Address addr, mlir::Location loc) const;
113
114 /// Turn an atomic-layout object into an r-value.
115 RValue convertAtomicTempToRValue(Address addr, AggValueSlot resultSlot,
116 SourceLocation loc, bool asValue) const;
117
118 /// Converts a rvalue to integer value.
119 mlir::Value convertRValueToInt(RValue rvalue, mlir::Location loc,
120 bool cmpxchg = false) const;
121
122 RValue convertToValueOrAtomic(mlir::Value intVal, AggValueSlot resultSlot,
123 SourceLocation loc, bool asValue,
124 bool cmpxchg = false) const;
125
126 /// Copy an atomic r-value into atomic-layout memory.
127 void emitCopyIntoMemory(RValue rvalue) const;
128
129 /// Project an l-value down to the value field.
130 LValue projectValue() const {
131 assert(lvalue.isSimple());
132 Address addr = getAtomicAddress();
133 if (hasPadding())
134 addr = cgf.getBuilder().createGetMember(loc, addr, /*name=*/"value",
135 /*index=*/0);
136
138 return LValue::makeAddr(addr, getValueType(), lvalue.getBaseInfo());
139 }
140
141 /// Emits atomic load.
142 /// \returns Loaded value.
143 RValue emitAtomicLoad(AggValueSlot resultSlot, SourceLocation loc,
144 bool asValue, cir::MemOrder order, bool isVolatile);
145
146 /// Materialize an atomic r-value in atomic-layout memory.
147 Address materializeRValue(RValue rvalue, mlir::Location loc) const;
148
149 /// Creates temp alloca for intermediate operations on atomic value.
150 Address createTempAlloca() const;
151
152private:
153 bool requiresMemSetZero(mlir::Type ty) const;
154
155 /// Emits atomic load as a CIR operation.
156 mlir::Value emitAtomicLoadOp(cir::MemOrder order, bool isVolatile,
157 bool cmpxchg = false);
158};
159} // namespace
160
161// This function emits any expression (scalar, complex, or aggregate)
162// into a temporary alloca.
164 Address declPtr = cgf.createMemTemp(
165 e->getType(), cgf.getLoc(e->getSourceRange()), ".atomictmp");
166 cgf.emitAnyExprToMem(e, declPtr, e->getType().getQualifiers(),
167 /*Init*/ true);
168 return declPtr;
169}
170
171/// Does a store of the given IR type modify the full expected width?
172static bool isFullSizeType(CIRGenModule &cgm, mlir::Type ty,
173 uint64_t expectedSize) {
174 return cgm.getDataLayout().getTypeStoreSize(ty) * 8 == expectedSize;
175}
176
177/// Does the atomic type require memsetting to zero before initialization?
178///
179/// The IR type is provided as a way of making certain queries faster.
180bool AtomicInfo::requiresMemSetZero(mlir::Type ty) const {
181 // If the atomic type has size padding, we definitely need a memset.
182 if (hasPadding())
183 return true;
184
185 // Otherwise, do some simple heuristics to try to avoid it:
186 switch (getEvaluationKind()) {
187 // For scalars and complexes, check whether the store size of the
188 // type uses the full size.
189 case cir::TEK_Scalar:
190 return !isFullSizeType(cgf.cgm, ty, atomicSizeInBits);
191 case cir::TEK_Complex:
192 return !isFullSizeType(cgf.cgm,
193 mlir::cast<cir::ComplexType>(ty).getElementType(),
194 atomicSizeInBits / 2);
195 // Padding in structs has an undefined bit pattern. User beware.
197 return false;
198 }
199 llvm_unreachable("bad evaluation kind");
200}
201
202Address AtomicInfo::convertToAtomicIntPointer(Address addr,
203 mlir::Location loc) const {
204 mlir::Type ty = addr.getElementType();
205 uint64_t sourceSizeInBits = cgf.cgm.getDataLayout().getTypeSizeInBits(ty);
206 if (sourceSizeInBits != atomicSizeInBits) {
207 CIRGenBuilderTy &builder = cgf.getBuilder();
208
209 Address tmp = createTempAlloca();
210 mlir::Value zero = builder.getConstInt(loc, cgf.cgm.uInt8Ty, 0);
211 unsigned size =
212 cgf.getContext().toCharUnitsFromBits(atomicSizeInBits).getQuantity();
213 mlir::Value memSetSize = builder.getConstInt(loc, cgf.cgm.uInt64Ty, size);
214 addr = addr.withElementType(builder, cgf.cgm.voidTy);
215 builder.createMemSet(loc, addr, zero, memSetSize);
216
217 tmp = tmp.withElementType(builder, cgf.cgm.voidTy);
218 builder.createMemCpy(
219 loc, tmp.getPointer(), addr.getPointer(),
220 builder.getConstInt(loc, cgf.cgm.uInt64Ty,
221 std::min(atomicSizeInBits, sourceSizeInBits) / 8));
222 addr = tmp;
223 }
224
225 return castToAtomicIntPointer(addr);
226}
227
228RValue AtomicInfo::convertAtomicTempToRValue(Address addr,
229 AggValueSlot resultSlot,
230 SourceLocation loc,
231 bool asValue) const {
232 if (lvalue.isSimple()) {
233 if (evaluationKind == TEK_Aggregate)
234 return resultSlot.asRValue();
235
236 // Drill into the padding structure if we have one.
237 if (hasPadding()) {
238 cgf.cgm.errorNYI(loc,
239 "AtomicInfo::convertAtomicTempToRValue: hasPadding");
240 return RValue::get(nullptr);
241 }
242
243 // Otherwise, just convert the temporary to an r-value using the
244 // normal conversion routine.
245 return cgf.convertTempToRValue(addr, getValueType(), loc);
246 }
247
248 cgf.cgm.errorNYI(
249 loc, "AtomicInfo::convertAtomicTempToRValue: lvalue is not simple");
250 return RValue::get(nullptr);
251}
252
253RValue AtomicInfo::emitAtomicLoad(AggValueSlot resultSlot, SourceLocation loc,
254 bool asValue, cir::MemOrder order,
255 bool isVolatile) {
256 // Check whether we should use a library call.
257 if (shouldUseLibCall()) {
259 cgf.cgm.errorNYI(loc, "emitAtomicLoad: emit atomic lib call");
260 return RValue::get(nullptr);
261 }
262
263 // Okay, we're doing this natively.
264 mlir::Value loadOp = emitAtomicLoadOp(order, isVolatile);
265
266 // If we're ignoring an aggregate return, don't do anything.
267 if (getEvaluationKind() == TEK_Aggregate && resultSlot.isIgnored())
268 return RValue::getAggregate(Address::invalid(), false);
269
270 // Okay, turn that back into the original value or atomic (for non-simple
271 // lvalues) type.
272 return convertToValueOrAtomic(loadOp, resultSlot, loc, asValue);
273}
274
275Address AtomicInfo::createTempAlloca() const {
276 // Remove addrspace info from the atomic pointer element when making the
277 // alloca pointer element.
278 QualType tmpTy = (lvalue.isBitField() && valueSizeInBits > atomicSizeInBits)
279 ? valueTy
280 : atomicTy.getUnqualifiedType();
281 Address tempAlloca =
282 cgf.createMemTemp(tmpTy, getAtomicAlignment(), loc, "atomic-temp");
283
284 // Cast to pointer to value type for bitfields.
285 if (lvalue.isBitField()) {
286 cgf.cgm.errorNYI(loc, "AtomicInfo::createTempAlloca: bitfield lvalue");
287 }
288
289 return tempAlloca;
290}
291
292mlir::Value AtomicInfo::getScalarRValValueOrNull(RValue rvalue) const {
293 if (rvalue.isScalar() && (!hasPadding() || !lvalue.isSimple()))
294 return rvalue.getValue();
295 return nullptr;
296}
297
298Address AtomicInfo::castToAtomicIntPointer(Address addr) const {
299 auto intTy = mlir::dyn_cast<cir::IntType>(addr.getElementType());
300 // Don't bother with int casts if the integer size is the same.
301 if (intTy && intTy.getWidth() == atomicSizeInBits)
302 return addr;
303 auto ty = cgf.getBuilder().getUIntNTy(atomicSizeInBits);
304 return addr.withElementType(cgf.getBuilder(), ty);
305}
306
307bool AtomicInfo::emitMemSetZeroIfNecessary() const {
308 assert(lvalue.isSimple());
309 Address addr = lvalue.getAddress();
310 if (!requiresMemSetZero(addr.getElementType()))
311 return false;
312
313 addr = addr.withElementType(cgf.getBuilder(), cgf.cgm.voidTy);
314 mlir::Value zero = cgf.getBuilder().getConstInt(loc, cgf.cgm.uInt8Ty, 0);
315 mlir::Value memSetSize = cgf.getBuilder().getConstInt(
316 loc, cgf.cgm.uInt64Ty,
317 cgf.getContext().toCharUnitsFromBits(atomicSizeInBits).getQuantity());
318
319 cgf.getBuilder().createMemSet(loc, addr, zero, memSetSize);
320 return true;
321}
322
323/// Return true if \param valueTy is a type that should be casted to integer
324/// around the atomic memory operation. If \param cmpxchg is true, then the
325/// cast of a floating point type is made as that instruction can not have
326/// floating point operands. TODO: Allow compare-and-exchange and FP - see
327/// comment in CIRGenAtomicExpandPass.cpp.
328static bool shouldCastToInt(mlir::Type valueTy, bool cmpxchg) {
329 if (cir::isAnyFloatingPointType(valueTy))
330 return isa<cir::FP80Type>(valueTy) || cmpxchg;
331 return !isa<cir::IntType>(valueTy) && !isa<cir::PointerType>(valueTy);
332}
333
334mlir::Value AtomicInfo::emitAtomicLoadOp(cir::MemOrder order, bool isVolatile,
335 bool cmpxchg) {
336 Address addr = getAtomicAddress();
337 if (shouldCastToInt(addr.getElementType(), cmpxchg))
338 addr = castToAtomicIntPointer(addr);
339
340 cir::LoadOp op =
341 cgf.getBuilder().createLoad(loc, addr, /*isVolatile=*/isVolatile);
342 op.setMemOrder(order);
343
345 return op;
346}
347
348mlir::Value AtomicInfo::convertRValueToInt(RValue rvalue, mlir::Location loc,
349 bool cmpxchg) const {
350 // If we've got a scalar value of the right size, try to avoid going
351 // through memory. Floats get casted if needed by AtomicExpandPass.
352 if (mlir::Value value = getScalarRValValueOrNull(rvalue)) {
353 if (!shouldCastToInt(value.getType(), cmpxchg))
354 return cgf.emitToMemory(value, valueTy);
355
356 cgf.cgm.errorNYI(
357 loc, "AtomicInfo::convertRValueToInt: cast scalar rvalue to int");
358 return nullptr;
359 }
360
361 // Otherwise, we need to go through memory.
362 // Put the r-value in memory.
363 Address addr = materializeRValue(rvalue, loc);
364
365 // Cast the temporary to the atomic int type and pull a value out.
366 addr = castToAtomicIntPointer(addr);
367
368 return cgf.getBuilder().createLoad(loc, addr);
369}
370
371RValue AtomicInfo::convertToValueOrAtomic(mlir::Value intVal,
372 AggValueSlot resultSlot,
373 SourceLocation loc, bool asValue,
374 bool cmpxchg) const {
375 // Try not to in some easy cases.
376 assert((mlir::isa<cir::IntType, cir::PointerType, cir::FPTypeInterface>(
377 intVal.getType())) &&
378 "Expected integer, pointer or floating point value when converting "
379 "result.");
380 bool isWholeValue =
381 !lvalue.isBitField() || lvalue.getBitFieldInfo().size == valueSizeInBits;
382 if (getEvaluationKind() == TEK_Scalar &&
383 ((isWholeValue && !hasPadding()) || !asValue)) {
384 mlir::Type valTy = asValue ? cgf.convertTypeForMem(valueTy)
385 : getAtomicAddress().getElementType();
386 if (!shouldCastToInt(valTy, cmpxchg)) {
387 assert((!mlir::isa<cir::IntType>(valTy) || intVal.getType() == valTy) &&
388 "Different integer types.");
389 return RValue::get(cgf.emitFromMemory(intVal, valueTy));
390 }
391
392 cgf.cgm.errorNYI("convertToValueOrAtomic: convert through bitcast");
393 return RValue::get(nullptr);
394 }
395
396 // Create a temporary. This needs to be big enough to hold the
397 // atomic integer.
398 Address temp = Address::invalid();
399 bool tempIsVolatile = false;
400 if (asValue && getEvaluationKind() == TEK_Aggregate) {
401 assert(!resultSlot.isIgnored());
402 temp = resultSlot.getAddress();
403 tempIsVolatile = resultSlot.isVolatile();
404 } else {
405 temp = createTempAlloca();
406 }
407
408 // Slam the integer into the temporary.
409 Address castTemp = castToAtomicIntPointer(temp);
410 cgf.getBuilder().createStore(cgf.getLoc(loc), intVal, castTemp,
411 tempIsVolatile);
412 return convertAtomicTempToRValue(temp, resultSlot, loc, asValue);
413}
414
415/// Copy an r-value into memory as part of storing to an atomic type.
416/// This needs to create a bit-pattern suitable for atomic operations.
417void AtomicInfo::emitCopyIntoMemory(RValue rvalue) const {
418 assert(lvalue.isSimple());
419
420 // If we have an r-value, the rvalue should be of the atomic type,
421 // which means that the caller is responsible for having zeroed
422 // any padding. Just do an aggregate copy of that type.
423 if (rvalue.isAggregate()) {
424 cgf.cgm.errorNYI("copying aggregate into atomic lvalue");
425 return;
426 }
427
428 // Okay, otherwise we're copying stuff.
429
430 // Zero out the buffer if necessary.
431 emitMemSetZeroIfNecessary();
432
433 // Drill past the padding if present.
434 LValue tempLValue = projectValue();
435
436 // Okay, store the rvalue in.
437 if (rvalue.isScalar()) {
438 cgf.emitStoreOfScalar(rvalue.getValue(), tempLValue, /*isInit=*/true);
439 } else {
440 cgf.emitStoreOfComplex(loc, rvalue.getComplexValue(), tempLValue,
441 /*isInit=*/true);
442 }
443}
444
445/// Materialize an r-value into memory for the purposes of storing it
446/// to an atomic type.
447Address AtomicInfo::materializeRValue(RValue rvalue, mlir::Location loc) const {
448 // Aggregate r-values are already in memory, and EmitAtomicStore
449 // requires them to be values of the atomic type.
450 if (rvalue.isAggregate())
451 return rvalue.getAggregateAddress();
452
453 // Otherwise, make a temporary and materialize into it.
454 LValue tempLV = cgf.makeAddrLValue(createTempAlloca(), getAtomicType());
455 AtomicInfo atomics(cgf, tempLV, loc);
456
457 atomics.emitCopyIntoMemory(rvalue);
458 return tempLV.getAddress();
459}
460
461static void emitDefaultCaseLabel(CIRGenBuilderTy &builder, mlir::Location loc) {
462 mlir::ArrayAttr valuesAttr = builder.getArrayAttr({});
463 mlir::OpBuilder::InsertPoint insertPoint;
464 cir::CaseOp::create(builder, loc, valuesAttr, cir::CaseOpKind::Default,
465 insertPoint);
466 builder.restoreInsertionPoint(insertPoint);
467}
468
469// Create a "case" operation with the given list of orders as its values. Also
470// create the region that will hold the body of the switch-case label.
471static void emitMemOrderCaseLabel(CIRGenBuilderTy &builder, mlir::Location loc,
472 mlir::Type orderType,
475 for (cir::MemOrder order : orders)
476 orderAttrs.push_back(cir::IntAttr::get(orderType, static_cast<int>(order)));
477 mlir::ArrayAttr ordersAttr = builder.getArrayAttr(orderAttrs);
478
479 mlir::OpBuilder::InsertPoint insertPoint;
480 cir::CaseOp::create(builder, loc, ordersAttr, cir::CaseOpKind::Anyof,
481 insertPoint);
482 builder.restoreInsertionPoint(insertPoint);
483}
484
485static void emitAtomicCmpXchg(CIRGenFunction &cgf, AtomicExpr *e, bool isWeak,
486 Address dest, Address ptr, Address val1,
487 Address val2, uint64_t size,
488 cir::MemOrder successOrder,
489 cir::MemOrder failureOrder,
490 cir::SyncScopeKind scope) {
491 mlir::Location loc = cgf.getLoc(e->getSourceRange());
492
493 CIRGenBuilderTy &builder = cgf.getBuilder();
494 mlir::Value expected = builder.createLoad(loc, val1);
495 mlir::Value desired = builder.createLoad(loc, val2);
496
497 auto cmpxchg = cir::AtomicCmpXchgOp::create(
498 builder, loc, expected.getType(), builder.getBoolTy(), ptr.getPointer(),
499 expected, desired,
500 cir::MemOrderAttr::get(&cgf.getMLIRContext(), successOrder),
501 cir::MemOrderAttr::get(&cgf.getMLIRContext(), failureOrder),
502 cir::SyncScopeKindAttr::get(&cgf.getMLIRContext(), scope),
503 builder.getI64IntegerAttr(ptr.getAlignment().getAsAlign().value()));
504
505 cmpxchg.setIsVolatile(e->isVolatile());
506 cmpxchg.setWeak(isWeak);
507
508 mlir::Value failed = builder.createNot(cmpxchg.getSuccess());
509 cir::IfOp::create(builder, loc, failed, /*withElseRegion=*/false,
510 [&](mlir::OpBuilder &, mlir::Location) {
511 auto ptrTy = mlir::cast<cir::PointerType>(
512 val1.getPointer().getType());
513 if (val1.getElementType() != ptrTy.getPointee()) {
514 val1 = val1.withPointer(builder.createPtrBitcast(
515 val1.getPointer(), val1.getElementType()));
516 }
517 builder.createStore(loc, cmpxchg.getOld(), val1);
518 builder.createYield(loc);
519 });
520
521 // Update the memory at Dest with Success's value.
522 cgf.emitStoreOfScalar(cmpxchg.getSuccess(),
523 cgf.makeAddrLValue(dest, e->getType()),
524 /*isInit=*/false);
525}
526
528 bool isWeak, Address dest, Address ptr,
529 Address val1, Address val2,
530 Expr *failureOrderExpr, uint64_t size,
531 cir::MemOrder successOrder,
532 cir::SyncScopeKind scope) {
533 Expr::EvalResult failureOrderEval;
534 if (failureOrderExpr->EvaluateAsInt(failureOrderEval, cgf.getContext())) {
535 uint64_t failureOrderInt = failureOrderEval.Val.getInt().getZExtValue();
536
537 cir::MemOrder failureOrder;
538 if (!cir::isValidCIRAtomicOrderingCABI(failureOrderInt)) {
539 failureOrder = cir::MemOrder::Relaxed;
540 } else {
541 switch ((cir::MemOrder)failureOrderInt) {
542 case cir::MemOrder::Relaxed:
543 // 31.7.2.18: "The failure argument shall not be memory_order_release
544 // nor memory_order_acq_rel". Fallback to monotonic.
545 case cir::MemOrder::Release:
546 case cir::MemOrder::AcquireRelease:
547 failureOrder = cir::MemOrder::Relaxed;
548 break;
549 case cir::MemOrder::Consume:
550 case cir::MemOrder::Acquire:
551 failureOrder = cir::MemOrder::Acquire;
552 break;
553 case cir::MemOrder::SequentiallyConsistent:
554 failureOrder = cir::MemOrder::SequentiallyConsistent;
555 break;
556 }
557 }
558
559 // Prior to c++17, "the failure argument shall be no stronger than the
560 // success argument". This condition has been lifted and the only
561 // precondition is 31.7.2.18. Effectively treat this as a DR and skip
562 // language version checks.
563 emitAtomicCmpXchg(cgf, e, isWeak, dest, ptr, val1, val2, size, successOrder,
564 failureOrder, scope);
565 return;
566 }
567
568 // The failure memory order is not a compile time constant. The CIR atomic ops
569 // require a constant value, so that memory order is known at compile time. In
570 // this case, we can switch based on the memory order and call each variant
571 // individually.
572 mlir::Value failureOrderVal = cgf.emitScalarExpr(failureOrderExpr);
573 mlir::Location atomicLoc = cgf.getLoc(e->getSourceRange());
574 cir::SwitchOp::create(
575 cgf.getBuilder(), atomicLoc, failureOrderVal,
576 [&](mlir::OpBuilder &b, mlir::Location loc, mlir::OperationState &os) {
577 mlir::Block *switchBlock = cgf.getBuilder().getBlock();
578
579 // case cir::MemOrder::Relaxed:
580 // // 31.7.2.18: "The failure argument shall not be
581 // memory_order_release
582 // // nor memory_order_acq_rel". Fallback to monotonic.
583 // case cir::MemOrder::Release:
584 // case cir::MemOrder::AcquireRelease:
585 // Note: Since there are 3 options, this makes sense to just emit as a
586 // 'default', which prevents user code from 'falling off' of this,
587 // which seems reasonable. Also, 'relaxed' being the default behavior
588 // is also probably the least harmful.
589 emitDefaultCaseLabel(cgf.getBuilder(), atomicLoc);
590 emitAtomicCmpXchg(cgf, e, isWeak, dest, ptr, val1, val2, size,
591 successOrder, cir::MemOrder::Relaxed, scope);
592 cgf.getBuilder().createBreak(atomicLoc);
593 cgf.getBuilder().setInsertionPointToEnd(switchBlock);
594
595 // case cir::MemOrder::Consume:
596 // case cir::MemOrder::Acquire:
597 emitMemOrderCaseLabel(cgf.getBuilder(), loc, failureOrderVal.getType(),
598 {cir::MemOrder::Consume, cir::MemOrder::Acquire});
599 emitAtomicCmpXchg(cgf, e, isWeak, dest, ptr, val1, val2, size,
600 successOrder, cir::MemOrder::Acquire, scope);
601 cgf.getBuilder().createBreak(atomicLoc);
602 cgf.getBuilder().setInsertionPointToEnd(switchBlock);
603
604 // case cir::MemOrder::SequentiallyConsistent:
605 emitMemOrderCaseLabel(cgf.getBuilder(), loc, failureOrderVal.getType(),
606 {cir::MemOrder::SequentiallyConsistent});
607 emitAtomicCmpXchg(cgf, e, isWeak, dest, ptr, val1, val2, size,
608 successOrder, cir::MemOrder::SequentiallyConsistent,
609 scope);
610 cgf.getBuilder().createBreak(atomicLoc);
611 cgf.getBuilder().setInsertionPointToEnd(switchBlock);
612
613 cgf.getBuilder().createYield(atomicLoc);
614 });
615}
616
617// A version of the emitAtomicCmpXchgFailureSet function that ALSO checks
618// whether it is 'weak' or not (by adding an 'if' around it, and calling
619// emitAtomicCmpXchgFailureSet 2x).
621 CIRGenFunction &cgf, AtomicExpr *e, Expr *isWeakExpr, Address dest,
622 Address ptr, Address val1, Address val2, Expr *failureOrderExpr,
623 uint64_t size, cir::MemOrder successOrder, cir::SyncScopeKind scope) {
624 mlir::Value isWeakVal = cgf.emitScalarExpr(isWeakExpr);
625 // The AST seems to be inserting a 'bool' cast (even in C mode) here, so we'll
626 // just emit it like a scalar.
627 assert(isWeakVal.getType() == cgf.getBuilder().getBoolTy());
628 mlir::Location atomicLoc = cgf.getLoc(e->getSourceRange());
629
630 // Unlike classic compiler, we use an 'if' here instead of a switch, simply to
631 // make this more readable/logical, plus we don't allow switch over a bool in
632 // CIR.
633 cir::IfOp::create(
634 cgf.getBuilder(), atomicLoc, isWeakVal, /*elseRegion=*/true,
635 [&](mlir::OpBuilder &b, mlir::Location loc) {
636 emitAtomicCmpXchgFailureSet(cgf, e, /*isWeak=*/true, dest, ptr, val1,
637 val2, failureOrderExpr, size, successOrder,
638 scope);
639 cgf.getBuilder().createYield(atomicLoc);
640 },
641 [&](mlir::OpBuilder &b, mlir::Location loc) {
642 emitAtomicCmpXchgFailureSet(cgf, e, /*isWeak=*/false, dest, ptr, val1,
643 val2, failureOrderExpr, size, successOrder,
644 scope);
645 cgf.getBuilder().createYield(atomicLoc);
646 });
647}
648
650 Address ptr, Address val1, Address val2,
651 Expr *isWeakExpr, Expr *failureOrderExpr, int64_t size,
652 cir::MemOrder order, cir::SyncScopeKind scope) {
654 llvm::StringRef opName;
655
656 CIRGenBuilderTy &builder = cgf.getBuilder();
657 mlir::Location loc = cgf.getLoc(expr->getSourceRange());
658 auto orderAttr = cir::MemOrderAttr::get(builder.getContext(), order);
659 auto scopeAttr = cir::SyncScopeKindAttr::get(builder.getContext(), scope);
660 cir::AtomicFetchKindAttr fetchAttr;
661 bool fetchFirst = true;
662
663 auto handleFetchOp = [&](cir::AtomicFetchKind kind) {
664 opName = cir::AtomicFetchOp::getOperationName();
665 fetchAttr = cir::AtomicFetchKindAttr::get(builder.getContext(), kind);
666 };
667
668 switch (expr->getOp()) {
669 case AtomicExpr::AO__c11_atomic_init:
670 llvm_unreachable("already handled!");
671
672 case AtomicExpr::AO__c11_atomic_compare_exchange_strong:
673 emitAtomicCmpXchgFailureSet(cgf, expr, /*isWeak=*/false, dest, ptr, val1,
674 val2, failureOrderExpr, size, order, scope);
675 return;
676
677 case AtomicExpr::AO__c11_atomic_compare_exchange_weak:
678 emitAtomicCmpXchgFailureSet(cgf, expr, /*isWeak=*/true, dest, ptr, val1,
679 val2, failureOrderExpr, size, order, scope);
680 return;
681
682 case AtomicExpr::AO__atomic_compare_exchange:
683 case AtomicExpr::AO__atomic_compare_exchange_n:
684 case AtomicExpr::AO__scoped_atomic_compare_exchange:
685 case AtomicExpr::AO__scoped_atomic_compare_exchange_n: {
686 bool isWeak = false;
687 if (isWeakExpr->EvaluateAsBooleanCondition(isWeak, cgf.getContext())) {
688 emitAtomicCmpXchgFailureSet(cgf, expr, isWeak, dest, ptr, val1, val2,
689 failureOrderExpr, size, order, scope);
690 } else {
691 emitAtomicCmpXchgFailureSetCheckWeak(cgf, expr, isWeakExpr, dest, ptr,
692 val1, val2, failureOrderExpr, size,
693 order, scope);
694 }
695 return;
696 }
697
698 case AtomicExpr::AO__c11_atomic_load:
699 case AtomicExpr::AO__atomic_load_n:
700 case AtomicExpr::AO__atomic_load:
701 case AtomicExpr::AO__scoped_atomic_load_n:
702 case AtomicExpr::AO__scoped_atomic_load: {
703 cir::LoadOp load =
704 builder.createLoad(loc, ptr, /*isVolatile=*/expr->isVolatile());
705
706 load->setAttr("mem_order", orderAttr);
707 load->setAttr("sync_scope", scopeAttr);
708
709 builder.createStore(loc, load->getResult(0), dest);
710 return;
711 }
712
713 case AtomicExpr::AO__c11_atomic_store:
714 case AtomicExpr::AO__atomic_store_n:
715 case AtomicExpr::AO__atomic_store:
716 case AtomicExpr::AO__scoped_atomic_store:
717 case AtomicExpr::AO__scoped_atomic_store_n: {
718 cir::LoadOp loadVal1 = builder.createLoad(loc, val1);
719
721
722 builder.createStore(loc, loadVal1, ptr, expr->isVolatile(),
723 /*isNontemporal=*/false,
724 /*align=*/mlir::IntegerAttr{}, scopeAttr, orderAttr);
725 return;
726 }
727
728 case AtomicExpr::AO__c11_atomic_exchange:
729 case AtomicExpr::AO__atomic_exchange_n:
730 case AtomicExpr::AO__atomic_exchange:
731 case AtomicExpr::AO__scoped_atomic_exchange_n:
732 case AtomicExpr::AO__scoped_atomic_exchange:
733 opName = cir::AtomicXchgOp::getOperationName();
734 break;
735
736 case AtomicExpr::AO__atomic_add_fetch:
737 case AtomicExpr::AO__scoped_atomic_add_fetch:
738 fetchFirst = false;
739 [[fallthrough]];
740 case AtomicExpr::AO__c11_atomic_fetch_add:
741 case AtomicExpr::AO__atomic_fetch_add:
742 case AtomicExpr::AO__scoped_atomic_fetch_add:
743 handleFetchOp(cir::AtomicFetchKind::Add);
744 break;
745
746 case AtomicExpr::AO__atomic_sub_fetch:
747 case AtomicExpr::AO__scoped_atomic_sub_fetch:
748 fetchFirst = false;
749 [[fallthrough]];
750 case AtomicExpr::AO__c11_atomic_fetch_sub:
751 case AtomicExpr::AO__atomic_fetch_sub:
752 case AtomicExpr::AO__scoped_atomic_fetch_sub:
753 handleFetchOp(cir::AtomicFetchKind::Sub);
754 break;
755
756 case AtomicExpr::AO__atomic_min_fetch:
757 case AtomicExpr::AO__scoped_atomic_min_fetch:
758 fetchFirst = false;
759 [[fallthrough]];
760 case AtomicExpr::AO__c11_atomic_fetch_min:
761 case AtomicExpr::AO__atomic_fetch_min:
762 case AtomicExpr::AO__scoped_atomic_fetch_min:
763 handleFetchOp(cir::AtomicFetchKind::Min);
764 break;
765
766 case AtomicExpr::AO__atomic_fetch_fminimum:
767 case AtomicExpr::AO__scoped_atomic_fetch_fminimum:
768 assert(expr->getValueType()->isFloatingType() &&
769 "fminimum operations only support floating-point types");
770 handleFetchOp(cir::AtomicFetchKind::Minimum);
771 break;
772
773 case AtomicExpr::AO__atomic_fetch_fminimum_num:
774 case AtomicExpr::AO__scoped_atomic_fetch_fminimum_num:
775 assert(expr->getValueType()->isFloatingType() &&
776 "fminimum_num operations only support floating-point types");
777 handleFetchOp(cir::AtomicFetchKind::MinimumNum);
778 break;
779
780 case AtomicExpr::AO__atomic_max_fetch:
781 case AtomicExpr::AO__scoped_atomic_max_fetch:
782 fetchFirst = false;
783 [[fallthrough]];
784 case AtomicExpr::AO__c11_atomic_fetch_max:
785 case AtomicExpr::AO__atomic_fetch_max:
786 case AtomicExpr::AO__scoped_atomic_fetch_max:
787 handleFetchOp(cir::AtomicFetchKind::Max);
788 break;
789
790 case AtomicExpr::AO__atomic_fetch_fmaximum:
791 case AtomicExpr::AO__scoped_atomic_fetch_fmaximum:
792 assert(expr->getValueType()->isFloatingType() &&
793 "fmaximum operations only support floating-point types");
794 handleFetchOp(cir::AtomicFetchKind::Maximum);
795 break;
796
797 case AtomicExpr::AO__atomic_fetch_fmaximum_num:
798 case AtomicExpr::AO__scoped_atomic_fetch_fmaximum_num:
799 assert(expr->getValueType()->isFloatingType() &&
800 "fmaximum_num operations only support floating-point types");
801 handleFetchOp(cir::AtomicFetchKind::MaximumNum);
802 break;
803
804 case AtomicExpr::AO__atomic_and_fetch:
805 case AtomicExpr::AO__scoped_atomic_and_fetch:
806 fetchFirst = false;
807 [[fallthrough]];
808 case AtomicExpr::AO__c11_atomic_fetch_and:
809 case AtomicExpr::AO__atomic_fetch_and:
810 case AtomicExpr::AO__scoped_atomic_fetch_and:
811 handleFetchOp(cir::AtomicFetchKind::And);
812 break;
813
814 case AtomicExpr::AO__atomic_or_fetch:
815 case AtomicExpr::AO__scoped_atomic_or_fetch:
816 fetchFirst = false;
817 [[fallthrough]];
818 case AtomicExpr::AO__c11_atomic_fetch_or:
819 case AtomicExpr::AO__atomic_fetch_or:
820 case AtomicExpr::AO__scoped_atomic_fetch_or:
821 handleFetchOp(cir::AtomicFetchKind::Or);
822 break;
823
824 case AtomicExpr::AO__atomic_xor_fetch:
825 case AtomicExpr::AO__scoped_atomic_xor_fetch:
826 fetchFirst = false;
827 [[fallthrough]];
828 case AtomicExpr::AO__c11_atomic_fetch_xor:
829 case AtomicExpr::AO__atomic_fetch_xor:
830 case AtomicExpr::AO__scoped_atomic_fetch_xor:
831 handleFetchOp(cir::AtomicFetchKind::Xor);
832 break;
833
834 case AtomicExpr::AO__atomic_nand_fetch:
835 case AtomicExpr::AO__scoped_atomic_nand_fetch:
836 fetchFirst = false;
837 [[fallthrough]];
838 case AtomicExpr::AO__c11_atomic_fetch_nand:
839 case AtomicExpr::AO__atomic_fetch_nand:
840 case AtomicExpr::AO__scoped_atomic_fetch_nand:
841 handleFetchOp(cir::AtomicFetchKind::Nand);
842 break;
843
844 case AtomicExpr::AO__atomic_test_and_set: {
845 auto op = cir::AtomicTestAndSetOp::create(
846 builder, loc, ptr.getPointer(), order,
847 builder.getI64IntegerAttr(ptr.getAlignment().getQuantity()),
848 expr->isVolatile());
849 builder.createStore(loc, op, dest);
850 return;
851 }
852
853 case AtomicExpr::AO__atomic_clear: {
854 cir::AtomicClearOp::create(
855 builder, loc, ptr.getPointer(), order,
856 builder.getI64IntegerAttr(ptr.getAlignment().getQuantity()),
857 expr->isVolatile());
858 return;
859 }
860
861 case AtomicExpr::AO__atomic_fetch_uinc:
862 case AtomicExpr::AO__scoped_atomic_fetch_uinc:
863 handleFetchOp(cir::AtomicFetchKind::UIncWrap);
864 break;
865
866 case AtomicExpr::AO__atomic_fetch_udec:
867 case AtomicExpr::AO__scoped_atomic_fetch_udec:
868 handleFetchOp(cir::AtomicFetchKind::UDecWrap);
869 break;
870
871 case AtomicExpr::AO__opencl_atomic_init:
872
873 case AtomicExpr::AO__hip_atomic_compare_exchange_strong:
874 case AtomicExpr::AO__opencl_atomic_compare_exchange_strong:
875
876 case AtomicExpr::AO__opencl_atomic_compare_exchange_weak:
877 case AtomicExpr::AO__hip_atomic_compare_exchange_weak:
878
879 case AtomicExpr::AO__opencl_atomic_load:
880 case AtomicExpr::AO__hip_atomic_load:
881
882 case AtomicExpr::AO__opencl_atomic_store:
883 case AtomicExpr::AO__hip_atomic_store:
884
885 case AtomicExpr::AO__hip_atomic_exchange:
886 case AtomicExpr::AO__opencl_atomic_exchange:
887
888 case AtomicExpr::AO__hip_atomic_fetch_add:
889 case AtomicExpr::AO__opencl_atomic_fetch_add:
890
891 case AtomicExpr::AO__hip_atomic_fetch_sub:
892 case AtomicExpr::AO__opencl_atomic_fetch_sub:
893
894 case AtomicExpr::AO__hip_atomic_fetch_min:
895 case AtomicExpr::AO__opencl_atomic_fetch_min:
896
897 case AtomicExpr::AO__hip_atomic_fetch_max:
898 case AtomicExpr::AO__opencl_atomic_fetch_max:
899
900 case AtomicExpr::AO__hip_atomic_fetch_and:
901 case AtomicExpr::AO__opencl_atomic_fetch_and:
902
903 case AtomicExpr::AO__hip_atomic_fetch_or:
904 case AtomicExpr::AO__opencl_atomic_fetch_or:
905
906 case AtomicExpr::AO__hip_atomic_fetch_xor:
907 case AtomicExpr::AO__opencl_atomic_fetch_xor:
908
909 cgf.cgm.errorNYI(expr->getSourceRange(), "emitAtomicOp: expr op NYI");
910 return;
911 }
912
913 assert(!opName.empty() && "expected operation name to build");
914 mlir::Value loadVal1 = builder.createLoad(loc, val1);
915
916 SmallVector<mlir::Value> atomicOperands = {ptr.getPointer(), loadVal1};
917 SmallVector<mlir::Type> atomicResTys = {loadVal1.getType()};
918 mlir::Operation *rmwOp = builder.create(loc, builder.getStringAttr(opName),
919 atomicOperands, atomicResTys);
920
921 if (fetchAttr)
922 rmwOp->setAttr("binop", fetchAttr);
923 rmwOp->setAttr("mem_order", orderAttr);
924 rmwOp->setAttr("sync_scope", scopeAttr);
925 if (expr->isVolatile())
926 rmwOp->setAttr("is_volatile", builder.getUnitAttr());
927 if (fetchFirst && opName == cir::AtomicFetchOp::getOperationName())
928 rmwOp->setAttr("fetch_first", builder.getUnitAttr());
929
930 mlir::Value result = rmwOp->getResult(0);
931
932 builder.createStore(loc, result, dest);
933}
934
935// Map clang sync scope to CIR sync scope.
936static cir::SyncScopeKind convertSyncScopeToCIR(CIRGenFunction &cgf,
937 SourceRange range,
938 clang::SyncScope scope) {
939 switch (scope) {
941 return cir::SyncScopeKind::SingleThread;
943 return cir::SyncScopeKind::System;
945 return cir::SyncScopeKind::Device;
947 return cir::SyncScopeKind::Workgroup;
949 return cir::SyncScopeKind::Wavefront;
951 return cir::SyncScopeKind::Cluster;
952
954 return cir::SyncScopeKind::HIPSingleThread;
956 return cir::SyncScopeKind::HIPSystem;
958 return cir::SyncScopeKind::HIPAgent;
960 return cir::SyncScopeKind::HIPWorkgroup;
962 return cir::SyncScopeKind::HIPWavefront;
964 return cir::SyncScopeKind::HIPCluster;
965
967 return cir::SyncScopeKind::OpenCLWorkGroup;
969 return cir::SyncScopeKind::OpenCLDevice;
971 return cir::SyncScopeKind::OpenCLAllSVMDevices;
973 return cir::SyncScopeKind::OpenCLSubGroup;
974 }
975
976 llvm_unreachable("unhandled sync scope");
977}
978
980 Address ptr, Address val1, Address val2,
981 Expr *isWeakExpr, Expr *failureOrderExpr, int64_t size,
982 cir::MemOrder order,
983 const std::optional<Expr::EvalResult> &scopeConst,
984 mlir::Value scopeValue) {
985 std::unique_ptr<AtomicScopeModel> scopeModel = expr->getScopeModel();
986
987 if (!scopeModel) {
988 emitAtomicOp(cgf, expr, dest, ptr, val1, val2, isWeakExpr, failureOrderExpr,
989 size, order, cir::SyncScopeKind::System);
990 return;
991 }
992
993 if (scopeConst.has_value()) {
994 cir::SyncScopeKind mappedScope = convertSyncScopeToCIR(
995 cgf, expr->getScope()->getSourceRange(),
996 scopeModel->map(scopeConst->Val.getInt().getZExtValue()));
997 emitAtomicOp(cgf, expr, dest, ptr, val1, val2, isWeakExpr, failureOrderExpr,
998 size, order, mappedScope);
999 return;
1000 }
1001
1002 // The sync scope is not a compile-time constant. Emit a switch statement to
1003 // handle each possible value of the sync scope.
1004 CIRGenBuilderTy &builder = cgf.getBuilder();
1005 mlir::Location loc = cgf.getLoc(expr->getSourceRange());
1006 llvm::ArrayRef<unsigned> allScopes = scopeModel->getRuntimeValues();
1007 unsigned fallback = scopeModel->getFallBackValue();
1008
1009 cir::SwitchOp::create(
1010 builder, loc, scopeValue,
1011 [&](mlir::OpBuilder &, mlir::Location loc, mlir::OperationState &) {
1012 mlir::Block *switchBlock = builder.getBlock();
1013
1014 // Default case -- use fallback scope
1015 cir::SyncScopeKind fallbackScope = convertSyncScopeToCIR(
1016 cgf, expr->getScope()->getSourceRange(), scopeModel->map(fallback));
1017 emitDefaultCaseLabel(builder, loc);
1018 emitAtomicOp(cgf, expr, dest, ptr, val1, val2, isWeakExpr,
1019 failureOrderExpr, size, order, fallbackScope);
1020 builder.createBreak(loc);
1021 builder.setInsertionPointToEnd(switchBlock);
1022
1023 // Emit a switch case for each non-fallback runtime scope value
1024 for (unsigned scope : allScopes) {
1025 if (scope == fallback)
1026 continue;
1027
1028 cir::SyncScopeKind cirScope = convertSyncScopeToCIR(
1029 cgf, expr->getScope()->getSourceRange(), scopeModel->map(scope));
1030
1031 mlir::ArrayAttr casesAttr = builder.getArrayAttr(
1032 {cir::IntAttr::get(scopeValue.getType(), scope)});
1033 mlir::OpBuilder::InsertPoint insertPoint;
1034 cir::CaseOp::create(builder, loc, casesAttr, cir::CaseOpKind::Equal,
1035 insertPoint);
1036
1037 builder.restoreInsertionPoint(insertPoint);
1038 emitAtomicOp(cgf, expr, dest, ptr, val1, val2, isWeakExpr,
1039 failureOrderExpr, size, order, cirScope);
1040 builder.createBreak(loc);
1041 builder.setInsertionPointToEnd(switchBlock);
1042 }
1043
1044 builder.createYield(loc);
1045 });
1046}
1047
1048static std::optional<cir::MemOrder>
1049getEffectiveAtomicMemOrder(cir::MemOrder oriOrder, bool isStore, bool isLoad,
1050 bool isFence) {
1051 // Some memory orders are not supported by partial atomic operation:
1052 // {memory_order_releaxed} is not valid for fence operations.
1053 // {memory_order_consume, memory_order_acquire} are not valid for write-only
1054 // operations.
1055 // {memory_order_release} is not valid for read-only operations.
1056 // {memory_order_acq_rel} is only valid for read-write operations.
1057 if (isStore) {
1058 if (oriOrder == cir::MemOrder::Consume ||
1059 oriOrder == cir::MemOrder::Acquire ||
1060 oriOrder == cir::MemOrder::AcquireRelease)
1061 return std::nullopt;
1062 } else if (isLoad) {
1063 if (oriOrder == cir::MemOrder::Release ||
1064 oriOrder == cir::MemOrder::AcquireRelease)
1065 return std::nullopt;
1066 } else if (isFence) {
1067 if (oriOrder == cir::MemOrder::Relaxed)
1068 return std::nullopt;
1069 }
1070 // memory_order_consume is not implemented, it is always treated like
1071 // memory_order_acquire
1072 if (oriOrder == cir::MemOrder::Consume)
1073 return cir::MemOrder::Acquire;
1074 return oriOrder;
1075}
1076
1078 CIRGenFunction &cgf, mlir::Value order, bool isStore, bool isLoad,
1079 bool isFence, llvm::function_ref<void(cir::MemOrder)> emitAtomicOpFn) {
1080 if (!order)
1081 return;
1082 // The memory order is not known at compile-time. The atomic operations
1083 // can't handle runtime memory orders; the memory order must be hard coded.
1084 // Generate a "switch" statement that converts a runtime value into a
1085 // compile-time value.
1086 CIRGenBuilderTy &builder = cgf.getBuilder();
1087 cir::SwitchOp::create(
1088 builder, order.getLoc(), order,
1089 [&](mlir::OpBuilder &, mlir::Location loc, mlir::OperationState &) {
1090 mlir::Block *switchBlock = builder.getBlock();
1091
1092 auto emitMemOrderCase = [&](llvm::ArrayRef<cir::MemOrder> caseOrders) {
1093 // Checking there are same effective memory order for each case.
1094 for (int i = 1, e = caseOrders.size(); i < e; i++)
1095 assert((getEffectiveAtomicMemOrder(caseOrders[i - 1], isStore,
1096 isLoad, isFence) ==
1097 getEffectiveAtomicMemOrder(caseOrders[i], isStore, isLoad,
1098 isFence)) &&
1099 "Effective memory order must be same!");
1100 // Emit case label and atomic opeartion if neccessary.
1101 if (caseOrders.empty()) {
1102 emitDefaultCaseLabel(builder, loc);
1103 // There is no good way to report an unsupported memory order at
1104 // runtime, hence the fallback to memory_order_relaxed.
1105 if (!isFence)
1106 emitAtomicOpFn(cir::MemOrder::Relaxed);
1107 } else if (std::optional<cir::MemOrder> actualOrder =
1108 getEffectiveAtomicMemOrder(caseOrders[0], isStore,
1109 isLoad, isFence)) {
1110 // Included in default case.
1111 if (!isFence && actualOrder == cir::MemOrder::Relaxed)
1112 return;
1113 // Creating case operation for effective memory order. If there are
1114 // multiple cases in `caseOrders`, the actual order of each case
1115 // must be same, this needs to be guaranteed by the caller.
1116 emitMemOrderCaseLabel(builder, loc, order.getType(), caseOrders);
1117 emitAtomicOpFn(actualOrder.value());
1118 } else {
1119 // Do nothing if (!caseOrders.empty() && !actualOrder)
1120 return;
1121 }
1122 builder.createBreak(loc);
1123 builder.setInsertionPointToEnd(switchBlock);
1124 };
1125
1126 emitMemOrderCase(/*default:*/ {});
1127 emitMemOrderCase({cir::MemOrder::Relaxed});
1128 emitMemOrderCase({cir::MemOrder::Consume, cir::MemOrder::Acquire});
1129 emitMemOrderCase({cir::MemOrder::Release});
1130 emitMemOrderCase({cir::MemOrder::AcquireRelease});
1131 emitMemOrderCase({cir::MemOrder::SequentiallyConsistent});
1132
1133 builder.createYield(loc);
1134 });
1135}
1136
1138 const Expr *memOrder, bool isStore, bool isLoad, bool isFence,
1139 llvm::function_ref<void(cir::MemOrder)> emitAtomicOpFn) {
1140 // Emit the memory order operand, and try to evaluate it as a constant.
1141 Expr::EvalResult eval;
1142 if (memOrder->EvaluateAsInt(eval, getContext())) {
1143 uint64_t constOrder = eval.Val.getInt().getZExtValue();
1144 // We should not ever get to a case where the ordering isn't a valid CABI
1145 // value, but it's hard to enforce that in general.
1146 if (!cir::isValidCIRAtomicOrderingCABI(constOrder))
1147 return;
1148 cir::MemOrder oriOrder = static_cast<cir::MemOrder>(constOrder);
1149 if (std::optional<cir::MemOrder> actualOrder =
1150 getEffectiveAtomicMemOrder(oriOrder, isStore, isLoad, isFence))
1151 emitAtomicOpFn(actualOrder.value());
1152 return;
1153 }
1154
1155 // Otherwise, handle variable memory ordering. Emit `SwitchOp` to convert
1156 // dynamic value to static value.
1157 mlir::Value dynOrder = emitScalarExpr(memOrder);
1158 emitAtomicExprWithDynamicMemOrder(*this, dynOrder, isStore, isLoad, isFence,
1159 emitAtomicOpFn);
1160}
1161
1162static RValue emitAtomicLibCall(CIRGenFunction &cgf, llvm::StringRef funcName,
1163 QualType resultType, CallArgList &args) {
1164 const CIRGenFunctionInfo &fnInfo =
1165 cgf.cgm.getTypes().arrangeBuiltinFunctionCall(resultType, args);
1166 cir::FuncType fnTy = cgf.cgm.getTypes().getFunctionType(fnInfo);
1167
1168 mlir::NamedAttrList fnAttrs;
1170
1171 cir::FuncOp fn = cgf.cgm.createRuntimeFunction(fnTy, funcName, fnAttrs);
1172 auto callee = CIRGenCallee::forDirect(fn);
1173 return cgf.emitCall(fnInfo, callee, ReturnValueSlot(), args,
1174 /*isMustTail=*/false);
1175}
1176
1178 Address atomicPtr, Address dest,
1179 Address val1, uint64_t atomicTySize,
1180 QualType resultTy) {
1181 mlir::Location loc = cgf.getLoc(e->getSourceRange());
1182
1183 CallArgList args;
1184 // For non-optimized library calls, the size is the first parameter.
1185 args.add(
1186 RValue::get(cgf.getBuilder().getConstInt(loc, cgf.sizeTy, atomicTySize)),
1187 cgf.getContext().getSizeType());
1188
1189 // The atomic address is the second parameter.
1190 // The OpenCL atomic library functions only accept pointer arguments to
1191 // generic address space.
1192 auto castToGenericAddrSpace = [&](mlir::Value v, QualType pt) {
1193 if (!e->isOpenCL())
1194 return cgf.getBuilder().createPtrBitcast(v, cgf.voidTy);
1195
1197 cgf.cgm.errorNYI(loc, "emitLibCallForAtomicExpr: openCL");
1198 return cgf.getBuilder().createPtrBitcast(v, cgf.voidTy);
1199 };
1200 args.add(RValue::get(castToGenericAddrSpace(atomicPtr.emitRawPointer(),
1201 e->getPtr()->getType())),
1202 cgf.getContext().VoidPtrTy);
1203
1204 // The next 1-3 parameters are op-dependent.
1205 llvm::StringRef calleeName;
1206 QualType retTy;
1207 bool hasRetTy = false;
1208 switch (e->getOp()) {
1209 case AtomicExpr::AO__c11_atomic_init:
1210 case AtomicExpr::AO__opencl_atomic_init:
1211 llvm_unreachable("Already handled!");
1212
1213 // There is only one libcall for compare an exchange, because there is no
1214 // optimisation benefit possible from a libcall version of a weak compare
1215 // and exchange.
1216 // bool __atomic_compare_exchange(size_t size, void *mem, void *expected,
1217 // void *desired, int success, int failure)
1218 case AtomicExpr::AO__atomic_compare_exchange:
1219 case AtomicExpr::AO__atomic_compare_exchange_n:
1220 case AtomicExpr::AO__c11_atomic_compare_exchange_weak:
1221 case AtomicExpr::AO__c11_atomic_compare_exchange_strong:
1222 case AtomicExpr::AO__hip_atomic_compare_exchange_weak:
1223 case AtomicExpr::AO__hip_atomic_compare_exchange_strong:
1224 case AtomicExpr::AO__opencl_atomic_compare_exchange_weak:
1225 case AtomicExpr::AO__opencl_atomic_compare_exchange_strong:
1226 case AtomicExpr::AO__scoped_atomic_compare_exchange:
1227 case AtomicExpr::AO__scoped_atomic_compare_exchange_n:
1228 cgf.cgm.errorNYI(
1229 loc, "emitLibCallForAtomicExpr: atomic compare-and-exchange NYI");
1230 return RValue::get(nullptr);
1231
1232 // void __atomic_exchange(size_t size, void *mem, void *val, void *return,
1233 // int order)
1234 case AtomicExpr::AO__atomic_exchange:
1235 case AtomicExpr::AO__atomic_exchange_n:
1236 case AtomicExpr::AO__c11_atomic_exchange:
1237 case AtomicExpr::AO__hip_atomic_exchange:
1238 case AtomicExpr::AO__opencl_atomic_exchange:
1239 case AtomicExpr::AO__scoped_atomic_exchange:
1240 case AtomicExpr::AO__scoped_atomic_exchange_n:
1241 calleeName = "__atomic_exchange";
1242 args.add(RValue::get(castToGenericAddrSpace(val1.emitRawPointer(),
1243 e->getVal1()->getType())),
1244 cgf.getContext().VoidPtrTy);
1245 break;
1246
1247 // void __atomic_store(size_t size, void *mem, void *val, int order)
1248 case AtomicExpr::AO__atomic_store:
1249 case AtomicExpr::AO__atomic_store_n:
1250 case AtomicExpr::AO__c11_atomic_store:
1251 case AtomicExpr::AO__scoped_atomic_store:
1252 case AtomicExpr::AO__scoped_atomic_store_n: {
1253 calleeName = "__atomic_store";
1254 retTy = cgf.getContext().VoidTy;
1255 hasRetTy = true;
1256 args.add(RValue::get(castToGenericAddrSpace(val1.emitRawPointer(),
1257 e->getVal1()->getType())),
1258 cgf.getContext().VoidPtrTy);
1259 break;
1260 }
1261
1262 case AtomicExpr::AO__hip_atomic_store:
1263 case AtomicExpr::AO__opencl_atomic_store:
1264 cgf.cgm.errorNYI(loc,
1265 "emitLibCallForAtomicExpr: atomic store for hip/opencl");
1266 return RValue::get(nullptr);
1267
1268 // void __atomic_load(size_t size, void *mem, void *return, int order)
1269 case AtomicExpr::AO__atomic_load:
1270 case AtomicExpr::AO__atomic_load_n:
1271 case AtomicExpr::AO__c11_atomic_load:
1272 case AtomicExpr::AO__scoped_atomic_load:
1273 case AtomicExpr::AO__scoped_atomic_load_n: {
1274 calleeName = "__atomic_load";
1275 break;
1276 }
1277
1278 case AtomicExpr::AO__hip_atomic_load:
1279 case AtomicExpr::AO__opencl_atomic_load:
1280 cgf.cgm.errorNYI(loc,
1281 "emitLibCallForAtomicExpr: atomic load for hip/opencl");
1282 return RValue::get(nullptr);
1283
1284 case AtomicExpr::AO__atomic_fetch_fmaximum:
1285 case AtomicExpr::AO__atomic_fetch_fmaximum_num:
1286 case AtomicExpr::AO__atomic_fetch_fminimum:
1287 case AtomicExpr::AO__atomic_fetch_fminimum_num:
1288 case AtomicExpr::AO__scoped_atomic_fetch_fmaximum:
1289 case AtomicExpr::AO__scoped_atomic_fetch_fmaximum_num:
1290 case AtomicExpr::AO__scoped_atomic_fetch_fminimum:
1291 case AtomicExpr::AO__scoped_atomic_fetch_fminimum_num:
1292 cgf.cgm.errorNYI(
1293 loc, "emitLibCallForAtomicExpr: atomic fetch fmaximum/fminimum");
1294 return RValue::get(nullptr);
1295
1296 case AtomicExpr::AO__atomic_add_fetch:
1297 case AtomicExpr::AO__scoped_atomic_add_fetch:
1298 case AtomicExpr::AO__atomic_fetch_add:
1299 case AtomicExpr::AO__c11_atomic_fetch_add:
1300 case AtomicExpr::AO__hip_atomic_fetch_add:
1301 case AtomicExpr::AO__opencl_atomic_fetch_add:
1302 case AtomicExpr::AO__scoped_atomic_fetch_add:
1303 case AtomicExpr::AO__atomic_and_fetch:
1304 case AtomicExpr::AO__scoped_atomic_and_fetch:
1305 case AtomicExpr::AO__atomic_fetch_and:
1306 case AtomicExpr::AO__c11_atomic_fetch_and:
1307 case AtomicExpr::AO__hip_atomic_fetch_and:
1308 case AtomicExpr::AO__opencl_atomic_fetch_and:
1309 case AtomicExpr::AO__scoped_atomic_fetch_and:
1310 case AtomicExpr::AO__atomic_or_fetch:
1311 case AtomicExpr::AO__scoped_atomic_or_fetch:
1312 case AtomicExpr::AO__atomic_fetch_or:
1313 case AtomicExpr::AO__c11_atomic_fetch_or:
1314 case AtomicExpr::AO__hip_atomic_fetch_or:
1315 case AtomicExpr::AO__opencl_atomic_fetch_or:
1316 case AtomicExpr::AO__scoped_atomic_fetch_or:
1317 case AtomicExpr::AO__atomic_sub_fetch:
1318 case AtomicExpr::AO__scoped_atomic_sub_fetch:
1319 case AtomicExpr::AO__atomic_fetch_sub:
1320 case AtomicExpr::AO__c11_atomic_fetch_sub:
1321 case AtomicExpr::AO__hip_atomic_fetch_sub:
1322 case AtomicExpr::AO__opencl_atomic_fetch_sub:
1323 case AtomicExpr::AO__scoped_atomic_fetch_sub:
1324 case AtomicExpr::AO__atomic_xor_fetch:
1325 case AtomicExpr::AO__scoped_atomic_xor_fetch:
1326 case AtomicExpr::AO__atomic_fetch_xor:
1327 case AtomicExpr::AO__c11_atomic_fetch_xor:
1328 case AtomicExpr::AO__hip_atomic_fetch_xor:
1329 case AtomicExpr::AO__opencl_atomic_fetch_xor:
1330 case AtomicExpr::AO__scoped_atomic_fetch_xor:
1331 case AtomicExpr::AO__atomic_nand_fetch:
1332 case AtomicExpr::AO__atomic_fetch_nand:
1333 case AtomicExpr::AO__c11_atomic_fetch_nand:
1334 case AtomicExpr::AO__scoped_atomic_fetch_nand:
1335 case AtomicExpr::AO__scoped_atomic_nand_fetch:
1336 case AtomicExpr::AO__atomic_min_fetch:
1337 case AtomicExpr::AO__atomic_fetch_min:
1338 case AtomicExpr::AO__c11_atomic_fetch_min:
1339 case AtomicExpr::AO__hip_atomic_fetch_min:
1340 case AtomicExpr::AO__opencl_atomic_fetch_min:
1341 case AtomicExpr::AO__scoped_atomic_fetch_min:
1342 case AtomicExpr::AO__scoped_atomic_min_fetch:
1343 case AtomicExpr::AO__atomic_max_fetch:
1344 case AtomicExpr::AO__atomic_fetch_max:
1345 case AtomicExpr::AO__c11_atomic_fetch_max:
1346 case AtomicExpr::AO__hip_atomic_fetch_max:
1347 case AtomicExpr::AO__opencl_atomic_fetch_max:
1348 case AtomicExpr::AO__scoped_atomic_fetch_max:
1349 case AtomicExpr::AO__scoped_atomic_max_fetch:
1350 case AtomicExpr::AO__scoped_atomic_fetch_uinc:
1351 case AtomicExpr::AO__scoped_atomic_fetch_udec:
1352 case AtomicExpr::AO__atomic_test_and_set:
1353 case AtomicExpr::AO__atomic_clear:
1354 case AtomicExpr::AO__atomic_fetch_uinc:
1355 case AtomicExpr::AO__atomic_fetch_udec:
1356 llvm_unreachable("Integral atomic operations always become atomicrmw!");
1357 }
1358
1359 if (e->isOpenCL()) {
1361 cgf.cgm.errorNYI(loc, "emitLibCallForAtomicExpr: openCL");
1362 return RValue::get(nullptr);
1363 }
1364
1365 // By default, assume we return a value of the atomic type.
1366 if (!hasRetTy) {
1367 // Value is returned through parameter before the order.
1368 retTy = cgf.getContext().VoidTy;
1369 args.add(RValue::get(castToGenericAddrSpace(dest.emitRawPointer(), retTy)),
1370 cgf.getContext().VoidPtrTy);
1371 }
1372
1373 // Order is always the last parameter.
1374 args.add(RValue::get(cgf.emitScalarExpr(e->getOrder())),
1375 cgf.getContext().IntTy);
1376 if (e->isOpenCL()) {
1378 cgf.cgm.errorNYI(loc, "emitLibCallForAtomicExpr: openCL");
1379 return RValue::get(nullptr);
1380 }
1381
1382 RValue res = emitAtomicLibCall(cgf, calleeName, retTy, args);
1383
1384 // The value is returned directly from the libcall.
1385 if (e->isCmpXChg())
1386 return res;
1387
1388 if (resultTy->isVoidType())
1389 return RValue::get(nullptr);
1390
1391 return cgf.convertTempToRValue(
1392 dest.withElementType(cgf.getBuilder(), cgf.convertTypeForMem(resultTy)),
1393 resultTy, e->getExprLoc());
1394}
1395
1397 QualType atomicTy = e->getPtr()->getType()->getPointeeType();
1398 QualType memTy = atomicTy;
1399 if (const auto *ty = atomicTy->getAs<AtomicType>())
1400 memTy = ty->getValueType();
1401
1402 Expr *isWeakExpr = nullptr;
1403 Expr *orderFailExpr = nullptr;
1404
1405 Address val1 = Address::invalid();
1406 Address val2 = Address::invalid();
1407 Address dest = Address::invalid();
1409
1411 if (e->getOp() == AtomicExpr::AO__c11_atomic_init) {
1412 LValue lvalue = makeAddrLValue(ptr, atomicTy);
1413 emitAtomicInit(e->getVal1(), lvalue);
1414 return RValue::get(nullptr);
1415 }
1416
1417 TypeInfoChars typeInfo = getContext().getTypeInfoInChars(atomicTy);
1418 uint64_t size = typeInfo.Width.getQuantity();
1419
1420 // Emit the sync scope operand, and try to evaluate it as a constant.
1421 mlir::Value scope =
1422 e->getScopeModel() ? emitScalarExpr(e->getScope()) : nullptr;
1423 std::optional<Expr::EvalResult> scopeConst;
1424 if (Expr::EvalResult eval;
1425 e->getScopeModel() && e->getScope()->EvaluateAsInt(eval, getContext()))
1426 scopeConst.emplace(std::move(eval));
1427
1428 switch (e->getOp()) {
1429 default:
1430 cgm.errorNYI(e->getSourceRange(), "atomic op NYI");
1431 return RValue::get(nullptr);
1432
1433 case AtomicExpr::AO__c11_atomic_init:
1434 llvm_unreachable("already handled above with emitAtomicInit");
1435
1436 case AtomicExpr::AO__atomic_load_n:
1437 case AtomicExpr::AO__scoped_atomic_load_n:
1438 case AtomicExpr::AO__c11_atomic_load:
1439 case AtomicExpr::AO__atomic_test_and_set:
1440 case AtomicExpr::AO__atomic_clear:
1441 break;
1442
1443 case AtomicExpr::AO__atomic_load:
1444 case AtomicExpr::AO__scoped_atomic_load:
1445 dest = emitPointerWithAlignment(e->getVal1());
1446 break;
1447
1448 case AtomicExpr::AO__atomic_store:
1449 case AtomicExpr::AO__scoped_atomic_store:
1450 val1 = emitPointerWithAlignment(e->getVal1());
1451 break;
1452
1453 case AtomicExpr::AO__atomic_exchange:
1454 case AtomicExpr::AO__scoped_atomic_exchange:
1455 val1 = emitPointerWithAlignment(e->getVal1());
1456 dest = emitPointerWithAlignment(e->getVal2());
1457 break;
1458
1459 case AtomicExpr::AO__atomic_compare_exchange:
1460 case AtomicExpr::AO__atomic_compare_exchange_n:
1461 case AtomicExpr::AO__c11_atomic_compare_exchange_weak:
1462 case AtomicExpr::AO__c11_atomic_compare_exchange_strong:
1463 case AtomicExpr::AO__scoped_atomic_compare_exchange:
1464 case AtomicExpr::AO__scoped_atomic_compare_exchange_n:
1465 val1 = emitPointerWithAlignment(e->getVal1());
1466 if (e->getOp() == AtomicExpr::AO__atomic_compare_exchange ||
1467 e->getOp() == AtomicExpr::AO__scoped_atomic_compare_exchange)
1468 val2 = emitPointerWithAlignment(e->getVal2());
1469 else
1470 val2 = emitValToTemp(*this, e->getVal2());
1471 orderFailExpr = e->getOrderFail();
1472 if (e->getOp() == AtomicExpr::AO__atomic_compare_exchange_n ||
1473 e->getOp() == AtomicExpr::AO__atomic_compare_exchange ||
1474 e->getOp() == AtomicExpr::AO__scoped_atomic_compare_exchange_n ||
1475 e->getOp() == AtomicExpr::AO__scoped_atomic_compare_exchange)
1476 isWeakExpr = e->getWeak();
1477 break;
1478
1479 case AtomicExpr::AO__c11_atomic_fetch_add:
1480 case AtomicExpr::AO__c11_atomic_fetch_sub:
1481 if (memTy->isPointerType()) {
1482 // For pointer arithmetic, we're required to do a bit of math:
1483 // adding 1 to an int* is not the same as adding 1 to a uintptr_t.
1484 // ... but only for the C11 builtins. The GNU builtins expect the
1485 // user to multiply by sizeof(T).
1486 QualType val1Ty = e->getVal1()->getType();
1487 mlir::Location loc = getLoc(e->getSourceRange());
1488 mlir::Value val1Scalar = emitScalarExpr(e->getVal1());
1489 CharUnits pointeeIncAmt =
1490 getContext().getTypeSizeInChars(memTy->getPointeeType());
1491 mlir::Value scale = builder.getConstInt(loc, val1Scalar.getType(),
1492 pointeeIncAmt.getQuantity());
1493 val1Scalar = builder.createMul(loc, val1Scalar, scale);
1494 val1 = createMemTemp(val1Ty, loc, ".atomictmp");
1495 emitStoreOfScalar(val1Scalar, makeAddrLValue(val1, val1Ty),
1496 /*isInit=*/true);
1497 }
1498 [[fallthrough]];
1499 case AtomicExpr::AO__atomic_fetch_add:
1500 case AtomicExpr::AO__atomic_fetch_sub:
1501 case AtomicExpr::AO__atomic_add_fetch:
1502 case AtomicExpr::AO__atomic_sub_fetch:
1503 if (memTy->isPointerType()) {
1504 // Fetch-and-update atomic operation on pointers should treat the pointer
1505 // value as uintptr_t values
1506 if (!val1.isValid())
1507 val1 = emitValToTemp(*this, e->getVal1());
1508 ptr = ptr.withElementType(builder, val1.getElementType());
1509 break;
1510 }
1511 [[fallthrough]];
1512 case AtomicExpr::AO__atomic_fetch_max:
1513 case AtomicExpr::AO__atomic_fetch_min:
1514 case AtomicExpr::AO__atomic_max_fetch:
1515 case AtomicExpr::AO__atomic_min_fetch:
1516 case AtomicExpr::AO__c11_atomic_fetch_max:
1517 case AtomicExpr::AO__c11_atomic_fetch_min:
1518 case AtomicExpr::AO__scoped_atomic_fetch_add:
1519 case AtomicExpr::AO__scoped_atomic_fetch_max:
1520 case AtomicExpr::AO__scoped_atomic_fetch_min:
1521 case AtomicExpr::AO__scoped_atomic_fetch_sub:
1522 case AtomicExpr::AO__scoped_atomic_fetch_fminimum:
1523 case AtomicExpr::AO__scoped_atomic_fetch_fmaximum:
1524 case AtomicExpr::AO__scoped_atomic_fetch_fminimum_num:
1525 case AtomicExpr::AO__scoped_atomic_fetch_fmaximum_num:
1526 case AtomicExpr::AO__scoped_atomic_add_fetch:
1527 case AtomicExpr::AO__scoped_atomic_max_fetch:
1528 case AtomicExpr::AO__scoped_atomic_min_fetch:
1529 case AtomicExpr::AO__scoped_atomic_sub_fetch:
1530 [[fallthrough]];
1531
1532 case AtomicExpr::AO__atomic_fetch_and:
1533 case AtomicExpr::AO__atomic_fetch_nand:
1534 case AtomicExpr::AO__atomic_fetch_or:
1535 case AtomicExpr::AO__atomic_fetch_xor:
1536 case AtomicExpr::AO__atomic_and_fetch:
1537 case AtomicExpr::AO__atomic_nand_fetch:
1538 case AtomicExpr::AO__atomic_or_fetch:
1539 case AtomicExpr::AO__atomic_xor_fetch:
1540 case AtomicExpr::AO__atomic_exchange_n:
1541 case AtomicExpr::AO__atomic_store_n:
1542 case AtomicExpr::AO__c11_atomic_fetch_and:
1543 case AtomicExpr::AO__c11_atomic_fetch_nand:
1544 case AtomicExpr::AO__c11_atomic_fetch_or:
1545 case AtomicExpr::AO__c11_atomic_fetch_xor:
1546 case AtomicExpr::AO__c11_atomic_exchange:
1547 case AtomicExpr::AO__c11_atomic_store:
1548 case AtomicExpr::AO__scoped_atomic_fetch_and:
1549 case AtomicExpr::AO__scoped_atomic_fetch_nand:
1550 case AtomicExpr::AO__scoped_atomic_fetch_or:
1551 case AtomicExpr::AO__scoped_atomic_fetch_xor:
1552 case AtomicExpr::AO__scoped_atomic_and_fetch:
1553 case AtomicExpr::AO__scoped_atomic_nand_fetch:
1554 case AtomicExpr::AO__scoped_atomic_or_fetch:
1555 case AtomicExpr::AO__scoped_atomic_xor_fetch:
1556 case AtomicExpr::AO__scoped_atomic_store_n:
1557 case AtomicExpr::AO__scoped_atomic_exchange_n:
1558 case AtomicExpr::AO__atomic_fetch_uinc:
1559 case AtomicExpr::AO__atomic_fetch_udec:
1560 case AtomicExpr::AO__scoped_atomic_fetch_uinc:
1561 case AtomicExpr::AO__scoped_atomic_fetch_udec:
1562 case AtomicExpr::AO__atomic_fetch_fminimum:
1563 case AtomicExpr::AO__atomic_fetch_fmaximum:
1564 case AtomicExpr::AO__atomic_fetch_fminimum_num:
1565 case AtomicExpr::AO__atomic_fetch_fmaximum_num:
1566 val1 = emitValToTemp(*this, e->getVal1());
1567 break;
1568 }
1569
1570 QualType resultTy = e->getType().getUnqualifiedType();
1571
1572 bool shouldCastToIntPtrTy =
1574
1575 // The inlined atomics only function on iN types, where N is a power of 2. We
1576 // need to make sure (via temporaries if necessary) that all incoming values
1577 // are compatible.
1578 mlir::Location loc = getLoc(e->getSourceRange());
1579 LValue atomicValue = makeAddrLValue(ptr, atomicTy);
1580 AtomicInfo atomics(*this, atomicValue, loc);
1581
1582 if (shouldCastToIntPtrTy) {
1583 ptr = atomics.castToAtomicIntPointer(ptr);
1584 if (val1.isValid())
1585 val1 = atomics.convertToAtomicIntPointer(val1, loc);
1586 if (val2.isValid())
1587 val2 = atomics.convertToAtomicIntPointer(val2, loc);
1588 }
1589 if (dest.isValid()) {
1590 if (shouldCastToIntPtrTy)
1591 dest = atomics.castToAtomicIntPointer(dest);
1592 } else if (e->isCmpXChg()) {
1593 dest = createMemTemp(resultTy, loc, "cmpxchg.bool");
1594 } else if (e->getOp() == AtomicExpr::AO__atomic_test_and_set) {
1595 dest = createMemTemp(resultTy, loc, "test_and_set.bool");
1596 } else if (!resultTy->isVoidType()) {
1597 dest = atomics.createTempAlloca();
1598 if (shouldCastToIntPtrTy)
1599 dest = atomics.castToAtomicIntPointer(dest);
1600 }
1601
1602 bool powerOf2Size = (size & (size - 1)) == 0;
1603 bool useLibCall = !powerOf2Size || (size > 16);
1604
1605 // For atomics larger than 16 bytes, emit a libcall from the frontend. This
1606 // avoids the overhead of dealing with excessively-large value types in IR.
1607 // Non-power-of-2 values also lower to libcall here, as they are not currently
1608 // permitted in IR instructions (although that constraint could be relaxed in
1609 // the future). For other cases where a libcall is required on a given
1610 // platform, we let the backend handle it (this includes handling for all of
1611 // the size-optimized libcall variants, which are only valid up to 16 bytes.)
1612 //
1613 // See: https://llvm.org/docs/Atomics.html#libcalls-atomic
1614 if (useLibCall)
1615 return emitLibCallForAtomicExpr(*this, e, ptr, dest, val1, size, resultTy);
1616
1617 bool isStore = e->getOp() == AtomicExpr::AO__c11_atomic_store ||
1618 e->getOp() == AtomicExpr::AO__opencl_atomic_store ||
1619 e->getOp() == AtomicExpr::AO__hip_atomic_store ||
1620 e->getOp() == AtomicExpr::AO__atomic_store ||
1621 e->getOp() == AtomicExpr::AO__atomic_store_n ||
1622 e->getOp() == AtomicExpr::AO__scoped_atomic_store ||
1623 e->getOp() == AtomicExpr::AO__scoped_atomic_store_n ||
1624 e->getOp() == AtomicExpr::AO__atomic_clear;
1625 bool isLoad = e->getOp() == AtomicExpr::AO__c11_atomic_load ||
1626 e->getOp() == AtomicExpr::AO__opencl_atomic_load ||
1627 e->getOp() == AtomicExpr::AO__hip_atomic_load ||
1628 e->getOp() == AtomicExpr::AO__atomic_load ||
1629 e->getOp() == AtomicExpr::AO__atomic_load_n ||
1630 e->getOp() == AtomicExpr::AO__scoped_atomic_load ||
1631 e->getOp() == AtomicExpr::AO__scoped_atomic_load_n;
1632
1633 auto emitAtomicOpCallBackFn = [&](cir::MemOrder memOrder) {
1634 emitAtomicOp(*this, e, dest, ptr, val1, val2, isWeakExpr, orderFailExpr,
1635 size, memOrder, scopeConst, scope);
1636 };
1637 emitAtomicExprWithMemOrder(e->getOrder(), isStore, isLoad, /*isFence*/ false,
1638 emitAtomicOpCallBackFn);
1639
1640 if (resultTy->isVoidType())
1641 return RValue::get(nullptr);
1642
1643 return convertTempToRValue(
1644 dest.withElementType(builder, convertTypeForMem(resultTy)), resultTy,
1645 e->getExprLoc());
1646}
1647
1649 AggValueSlot slot) {
1650 if (lvalue.getType()->isAtomicType())
1651 return emitAtomicLoad(lvalue, loc, cir::MemOrder::SequentiallyConsistent,
1652 /*isVolatile=*/lvalue.isVolatileQualified(), slot);
1653 return emitAtomicLoad(lvalue, loc, cir::MemOrder::Acquire,
1654 /*isVolatile=*/true, slot);
1655}
1656
1658 cir::MemOrder order, bool isVolatile,
1659 AggValueSlot slot) {
1660 AtomicInfo info(*this, lvalue, getLoc(loc));
1661 return info.emitAtomicLoad(slot, loc, /*asValue=*/true, order, isVolatile);
1662}
1663
1664void CIRGenFunction::emitAtomicStore(RValue rvalue, LValue dest, bool isInit) {
1665 bool isVolatile = dest.isVolatileQualified();
1666 auto order = cir::MemOrder::SequentiallyConsistent;
1667 if (!dest.getType()->isAtomicType()) {
1669 }
1670 return emitAtomicStore(rvalue, dest, order, isVolatile, isInit);
1671}
1672
1673/// Emit a store to an l-value of atomic type.
1674///
1675/// Note that the r-value is expected to be an r-value of the atomic type; this
1676/// means that for aggregate r-values, it should include storage for any padding
1677/// that was necessary.
1679 cir::MemOrder order, bool isVolatile,
1680 bool isInit) {
1681 // If this is an aggregate r-value, it should agree in type except
1682 // maybe for address-space qualification.
1683 mlir::Location loc = dest.getPointer().getLoc();
1684 assert(!rvalue.isAggregate() ||
1686 dest.getAddress().getElementType());
1687
1688 AtomicInfo atomics(*this, dest, loc);
1689 LValue lvalue = atomics.getAtomicLValue();
1690
1691 if (lvalue.isSimple()) {
1692 // If this is an initialization, just put the value there normally.
1693 if (isInit) {
1694 atomics.emitCopyIntoMemory(rvalue);
1695 return;
1696 }
1697
1698 // Check whether we should use a library call.
1699 if (atomics.shouldUseLibCall()) {
1701 cgm.errorNYI(loc, "emitAtomicStore: atomic store with library call");
1702 return;
1703 }
1704
1705 // Okay, we're doing this natively.
1706 mlir::Value valueToStore = atomics.convertRValueToInt(rvalue, loc);
1707
1708 // Do the atomic store.
1709 Address addr = atomics.getAtomicAddress();
1710 if (mlir::Value value = atomics.getScalarRValValueOrNull(rvalue)) {
1711 if (shouldCastToInt(value.getType(), /*CmpXchg=*/false)) {
1712 addr = atomics.castToAtomicIntPointer(addr);
1713 valueToStore =
1714 builder.createIntCast(valueToStore, addr.getElementType());
1715 }
1716 }
1717 cir::StoreOp store = builder.createStore(loc, valueToStore, addr);
1718
1719 // Initializations don't need to be atomic.
1720 if (!isInit) {
1722 store.setMemOrder(order);
1723 }
1724
1725 // Other decoration.
1726 if (isVolatile)
1727 store.setIsVolatile(true);
1728
1730 return;
1731 }
1732
1733 cgm.errorNYI(loc, "emitAtomicStore: non-simple atomic lvalue");
1735}
1736
1738 AtomicInfo atomics(*this, dest, getLoc(init->getSourceRange()));
1739
1740 switch (atomics.getEvaluationKind()) {
1741 case cir::TEK_Scalar: {
1742 mlir::Value value = emitScalarExpr(init);
1743 atomics.emitCopyIntoMemory(RValue::get(value));
1744 return;
1745 }
1746
1747 case cir::TEK_Complex: {
1748 mlir::Value value = emitComplexExpr(init);
1749 atomics.emitCopyIntoMemory(RValue::get(value));
1750 return;
1751 }
1752
1753 case cir::TEK_Aggregate: {
1754 // Fix up the destination if the initializer isn't an expression
1755 // of atomic type.
1756 bool zeroed = false;
1757 if (!init->getType()->isAtomicType()) {
1758 zeroed = atomics.emitMemSetZeroIfNecessary();
1759 dest = atomics.projectValue();
1760 }
1761
1762 // Evaluate the expression directly into the destination.
1768
1769 emitAggExpr(init, slot);
1770 return;
1771 }
1772 }
1773
1774 llvm_unreachable("bad evaluation kind");
1775}
static bool shouldCastToInt(mlir::Type valueTy, bool cmpxchg)
Return true if.
static RValue emitLibCallForAtomicExpr(CIRGenFunction &cgf, AtomicExpr *e, Address atomicPtr, Address dest, Address val1, uint64_t atomicTySize, QualType resultTy)
static Address emitValToTemp(CIRGenFunction &cgf, Expr *e)
static void emitAtomicCmpXchgFailureSetCheckWeak(CIRGenFunction &cgf, AtomicExpr *e, Expr *isWeakExpr, Address dest, Address ptr, Address val1, Address val2, Expr *failureOrderExpr, uint64_t size, cir::MemOrder successOrder, cir::SyncScopeKind scope)
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)
static RValue emitAtomicLibCall(CIRGenFunction &cgf, llvm::StringRef funcName, QualType resultType, CallArgList &args)
*collection of selector each with an associated kind and an ordered *collection of selectors A selector has a kind
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:511
CanQualType VoidPtrTy
TypeInfo getTypeInfo(const Type *T) const
Get the size and alignment of the specified complete type in bits.
CanQualType IntTy
int64_t toBits(CharUnits CharSize) const
Convert a size in characters to a size in bits.
CanQualType VoidTy
QualType getSizeType() const
Return the unique type for "size_t" (C99 7.17), defined in <stddef.h>.
const TargetInfo & getTargetInfo() const
Definition ASTContext.h:947
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:6978
static std::unique_ptr< AtomicScopeModel > getScopeModel(AtomicOp Op)
Get atomic scope model for the atomic op code.
Definition Expr.h:7127
Expr * getVal2() const
Definition Expr.h:7029
Expr * getOrder() const
Definition Expr.h:7012
Expr * getScope() const
Definition Expr.h:7015
bool isCmpXChg() const
Definition Expr.h:7062
AtomicOp getOp() const
Definition Expr.h:7041
bool isOpenCL() const
Definition Expr.h:7090
Expr * getVal1() const
Definition Expr.h:7019
Expr * getPtr() const
Definition Expr.h:7009
Expr * getWeak() const
Definition Expr.h:7035
Expr * getOrderFail() const
Definition Expr.h:7025
bool isVolatile() const
Definition Expr.h:7058
Address withPointer(mlir::Value newPtr) const
Return address with different pointer, but same element type and alignment.
Definition Address.h:83
mlir::Value getPointer() const
Definition Address.h:98
mlir::Type getElementType() const
Definition Address.h:125
static Address invalid()
Definition Address.h:76
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:138
bool isValid() const
Definition Address.h:77
mlir::Value emitRawPointer() const
Return the pointer contained in this class after authenticating it and adding offset to it if necessa...
Definition Address.h:112
An aggregate value slot.
static AggValueSlot forLValue(const LValue &LV, IsDestructed_t isDestructed, IsAliased_t isAliased, Overlap_t mayOverlap, IsZeroed_t isZeroed=IsNotZeroed)
cir::MemCpyOp createMemCpy(mlir::Location loc, mlir::Value dst, mlir::Value src, mlir::Value len)
cir::LoadOp createLoad(mlir::Location loc, Address addr, bool isVolatile=false, bool isNontemporal=false)
cir::StoreOp createStore(mlir::Location loc, mlir::Value val, Address dst, bool isVolatile=false, bool isNontemporal=false, mlir::IntegerAttr align={}, cir::SyncScopeKindAttr scope={}, cir::MemOrderAttr order={})
cir::ConstantOp getConstInt(mlir::Location loc, llvm::APSInt intVal)
cir::MemSetOp createMemSet(mlir::Location loc, mlir::Value dst, mlir::Value val, mlir::Value len)
cir::IntType getUIntNTy(int n)
static CIRGenCallee forDirect(mlir::Operation *funcPtr, const CIRGenCalleeInfo &abstractInfo=CIRGenCalleeInfo())
Definition CIRGenCall.h:92
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)
RValue emitAtomicLoad(LValue lvalue, SourceLocation loc, AggValueSlot slot=AggValueSlot::ignored())
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 emitStoreOfComplex(mlir::Location loc, mlir::Value v, LValue dest, bool isInit)
EmitStoreOfComplex - Store a complex number into the specified l-value.
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
RValue emitCall(const CIRGenFunctionInfo &funcInfo, const CIRGenCallee &callee, ReturnValueSlot returnValue, const CallArgList &args, cir::CIRCallOpInterface *callOp, bool isMustTail, mlir::Location loc)
mlir::Value emitFromMemory(mlir::Value value, clang::QualType ty)
EmitFromMemory - Change a scalar value from its memory representation to its value representation.
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.
cir::FuncOp createRuntimeFunction(cir::FuncType ty, llvm::StringRef name, mlir::NamedAttrList extraAttrs={}, bool isLocal=false, bool assumeConvergent=false)
const cir::CIRDataLayout getDataLayout() const
const CIRGenFunctionInfo & arrangeBuiltinFunctionCall(QualType resultType, const CallArgList &args)
A builtin function is a freestanding function using the default C conventions.
cir::FuncType getFunctionType(const CIRGenFunctionInfo &info)
Get the CIR function type for.
void add(RValue rvalue, clang::QualType type)
Definition CIRGenCall.h:239
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
static RValue getAggregate(Address addr, bool isVolatile=false)
Convert an Address to an RValue.
mlir::Value getValue() const
Return the value of this scalar value.
Definition CIRGenValue.h:57
bool isScalar() const
Definition CIRGenValue.h:49
mlir::Value getComplexValue() const
Return the value of this complex value.
Definition CIRGenValue.h:63
Contains the address where the return value of a function can be stored, and whether the address is v...
Definition CIRGenCall.h:260
CharUnits - This is an opaque type for sizes expressed in character units.
Definition CharUnits.h:38
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:113
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:283
QualType getType() const
Definition Expr.h:145
A (possibly-)qualified type.
Definition TypeBase.h:938
Qualifiers getQualifiers() const
Retrieve the set of qualifiers applied to this type.
Definition TypeBase.h:8541
QualType getUnqualifiedType() const
Retrieve the unqualified variant of the given type, removing as little sugar as possible.
Definition TypeBase.h:8595
Encodes a location in the source.
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:858
bool isVoidType() const
Definition TypeBase.h:9110
bool isPointerType() const
Definition TypeBase.h:8738
QualType getPointeeType() const
If this is a pointer, ObjC object pointer, or block pointer, this returns the respective pointee.
Definition Type.cpp:789
bool isAtomicType() const
Definition TypeBase.h:8930
const T * getAs() const
Member-template getAs<specific type>'.
Definition TypeBase.h:9337
bool isValidCIRAtomicOrderingCABI(Int value)
const internal::VariadicDynCastAllOfMatcher< Stmt, Expr > expr
Matches expressions.
@ Address
A pointer to a ValueDecl.
Definition Primitives.h:28
Top level wrappers for InstallAPI frontend operations.
bool isa(CodeGen::Address addr)
Definition Address.h:330
SyncScope
Defines sync scope values used internally by clang.
Definition SyncScope.h:43
unsigned long uint64_t
static bool atomicInfoGetAtomicPointer()
static bool aggValueSlotGC()
static bool opLoadStoreAtomic()
static bool opLoadStoreTbaa()
static bool opFuncExtraAttrs()
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:666
APValue Val
Val - This is the value the expression can be folded to.
Definition Expr.h:668