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}
1175
1177 Address atomicPtr, Address dest,
1178 Address val1, uint64_t atomicTySize,
1179 QualType resultTy) {
1180 mlir::Location loc = cgf.getLoc(e->getSourceRange());
1181
1182 CallArgList args;
1183 // For non-optimized library calls, the size is the first parameter.
1184 args.add(
1185 RValue::get(cgf.getBuilder().getConstInt(loc, cgf.sizeTy, atomicTySize)),
1186 cgf.getContext().getSizeType());
1187
1188 // The atomic address is the second parameter.
1189 // The OpenCL atomic library functions only accept pointer arguments to
1190 // generic address space.
1191 auto castToGenericAddrSpace = [&](mlir::Value v, QualType pt) {
1192 if (!e->isOpenCL())
1193 return cgf.getBuilder().createPtrBitcast(v, cgf.voidTy);
1194
1196 cgf.cgm.errorNYI(loc, "emitLibCallForAtomicExpr: openCL");
1197 return cgf.getBuilder().createPtrBitcast(v, cgf.voidTy);
1198 };
1199 args.add(RValue::get(castToGenericAddrSpace(atomicPtr.emitRawPointer(),
1200 e->getPtr()->getType())),
1201 cgf.getContext().VoidPtrTy);
1202
1203 // The next 1-3 parameters are op-dependent.
1204 llvm::StringRef calleeName;
1205 QualType retTy;
1206 bool hasRetTy = false;
1207 switch (e->getOp()) {
1208 case AtomicExpr::AO__c11_atomic_init:
1209 case AtomicExpr::AO__opencl_atomic_init:
1210 llvm_unreachable("Already handled!");
1211
1212 // There is only one libcall for compare an exchange, because there is no
1213 // optimisation benefit possible from a libcall version of a weak compare
1214 // and exchange.
1215 // bool __atomic_compare_exchange(size_t size, void *mem, void *expected,
1216 // void *desired, int success, int failure)
1217 case AtomicExpr::AO__atomic_compare_exchange:
1218 case AtomicExpr::AO__atomic_compare_exchange_n:
1219 case AtomicExpr::AO__c11_atomic_compare_exchange_weak:
1220 case AtomicExpr::AO__c11_atomic_compare_exchange_strong:
1221 case AtomicExpr::AO__hip_atomic_compare_exchange_weak:
1222 case AtomicExpr::AO__hip_atomic_compare_exchange_strong:
1223 case AtomicExpr::AO__opencl_atomic_compare_exchange_weak:
1224 case AtomicExpr::AO__opencl_atomic_compare_exchange_strong:
1225 case AtomicExpr::AO__scoped_atomic_compare_exchange:
1226 case AtomicExpr::AO__scoped_atomic_compare_exchange_n:
1227 cgf.cgm.errorNYI(
1228 loc, "emitLibCallForAtomicExpr: atomic compare-and-exchange NYI");
1229 return RValue::get(nullptr);
1230
1231 // void __atomic_exchange(size_t size, void *mem, void *val, void *return,
1232 // int order)
1233 case AtomicExpr::AO__atomic_exchange:
1234 case AtomicExpr::AO__atomic_exchange_n:
1235 case AtomicExpr::AO__c11_atomic_exchange:
1236 case AtomicExpr::AO__hip_atomic_exchange:
1237 case AtomicExpr::AO__opencl_atomic_exchange:
1238 case AtomicExpr::AO__scoped_atomic_exchange:
1239 case AtomicExpr::AO__scoped_atomic_exchange_n:
1240 cgf.cgm.errorNYI(loc, "emitLibCallForAtomicExpr: atomic exchange NYI");
1241 return RValue::get(nullptr);
1242
1243 // void __atomic_store(size_t size, void *mem, void *val, int order)
1244 case AtomicExpr::AO__atomic_store:
1245 case AtomicExpr::AO__atomic_store_n:
1246 case AtomicExpr::AO__c11_atomic_store:
1247 case AtomicExpr::AO__scoped_atomic_store:
1248 case AtomicExpr::AO__scoped_atomic_store_n: {
1249 calleeName = "__atomic_store";
1250 retTy = cgf.getContext().VoidTy;
1251 hasRetTy = true;
1252 args.add(RValue::get(castToGenericAddrSpace(val1.emitRawPointer(),
1253 e->getVal1()->getType())),
1254 cgf.getContext().VoidPtrTy);
1255 break;
1256 }
1257
1258 case AtomicExpr::AO__hip_atomic_store:
1259 case AtomicExpr::AO__opencl_atomic_store:
1260 cgf.cgm.errorNYI(loc,
1261 "emitLibCallForAtomicExpr: atomic store for hip/opencl");
1262 return RValue::get(nullptr);
1263
1264 // void __atomic_load(size_t size, void *mem, void *return, int order)
1265 case AtomicExpr::AO__atomic_load:
1266 case AtomicExpr::AO__atomic_load_n:
1267 case AtomicExpr::AO__c11_atomic_load:
1268 case AtomicExpr::AO__scoped_atomic_load:
1269 case AtomicExpr::AO__scoped_atomic_load_n: {
1270 calleeName = "__atomic_load";
1271 break;
1272 }
1273
1274 case AtomicExpr::AO__hip_atomic_load:
1275 case AtomicExpr::AO__opencl_atomic_load:
1276 cgf.cgm.errorNYI(loc,
1277 "emitLibCallForAtomicExpr: atomic load for hip/opencl");
1278 return RValue::get(nullptr);
1279
1280 case AtomicExpr::AO__atomic_fetch_fmaximum:
1281 case AtomicExpr::AO__atomic_fetch_fmaximum_num:
1282 case AtomicExpr::AO__atomic_fetch_fminimum:
1283 case AtomicExpr::AO__atomic_fetch_fminimum_num:
1284 case AtomicExpr::AO__scoped_atomic_fetch_fmaximum:
1285 case AtomicExpr::AO__scoped_atomic_fetch_fmaximum_num:
1286 case AtomicExpr::AO__scoped_atomic_fetch_fminimum:
1287 case AtomicExpr::AO__scoped_atomic_fetch_fminimum_num:
1288 cgf.cgm.errorNYI(
1289 loc, "emitLibCallForAtomicExpr: atomic fetch fmaximum/fminimum");
1290 return RValue::get(nullptr);
1291
1292 case AtomicExpr::AO__atomic_add_fetch:
1293 case AtomicExpr::AO__scoped_atomic_add_fetch:
1294 case AtomicExpr::AO__atomic_fetch_add:
1295 case AtomicExpr::AO__c11_atomic_fetch_add:
1296 case AtomicExpr::AO__hip_atomic_fetch_add:
1297 case AtomicExpr::AO__opencl_atomic_fetch_add:
1298 case AtomicExpr::AO__scoped_atomic_fetch_add:
1299 case AtomicExpr::AO__atomic_and_fetch:
1300 case AtomicExpr::AO__scoped_atomic_and_fetch:
1301 case AtomicExpr::AO__atomic_fetch_and:
1302 case AtomicExpr::AO__c11_atomic_fetch_and:
1303 case AtomicExpr::AO__hip_atomic_fetch_and:
1304 case AtomicExpr::AO__opencl_atomic_fetch_and:
1305 case AtomicExpr::AO__scoped_atomic_fetch_and:
1306 case AtomicExpr::AO__atomic_or_fetch:
1307 case AtomicExpr::AO__scoped_atomic_or_fetch:
1308 case AtomicExpr::AO__atomic_fetch_or:
1309 case AtomicExpr::AO__c11_atomic_fetch_or:
1310 case AtomicExpr::AO__hip_atomic_fetch_or:
1311 case AtomicExpr::AO__opencl_atomic_fetch_or:
1312 case AtomicExpr::AO__scoped_atomic_fetch_or:
1313 case AtomicExpr::AO__atomic_sub_fetch:
1314 case AtomicExpr::AO__scoped_atomic_sub_fetch:
1315 case AtomicExpr::AO__atomic_fetch_sub:
1316 case AtomicExpr::AO__c11_atomic_fetch_sub:
1317 case AtomicExpr::AO__hip_atomic_fetch_sub:
1318 case AtomicExpr::AO__opencl_atomic_fetch_sub:
1319 case AtomicExpr::AO__scoped_atomic_fetch_sub:
1320 case AtomicExpr::AO__atomic_xor_fetch:
1321 case AtomicExpr::AO__scoped_atomic_xor_fetch:
1322 case AtomicExpr::AO__atomic_fetch_xor:
1323 case AtomicExpr::AO__c11_atomic_fetch_xor:
1324 case AtomicExpr::AO__hip_atomic_fetch_xor:
1325 case AtomicExpr::AO__opencl_atomic_fetch_xor:
1326 case AtomicExpr::AO__scoped_atomic_fetch_xor:
1327 case AtomicExpr::AO__atomic_nand_fetch:
1328 case AtomicExpr::AO__atomic_fetch_nand:
1329 case AtomicExpr::AO__c11_atomic_fetch_nand:
1330 case AtomicExpr::AO__scoped_atomic_fetch_nand:
1331 case AtomicExpr::AO__scoped_atomic_nand_fetch:
1332 case AtomicExpr::AO__atomic_min_fetch:
1333 case AtomicExpr::AO__atomic_fetch_min:
1334 case AtomicExpr::AO__c11_atomic_fetch_min:
1335 case AtomicExpr::AO__hip_atomic_fetch_min:
1336 case AtomicExpr::AO__opencl_atomic_fetch_min:
1337 case AtomicExpr::AO__scoped_atomic_fetch_min:
1338 case AtomicExpr::AO__scoped_atomic_min_fetch:
1339 case AtomicExpr::AO__atomic_max_fetch:
1340 case AtomicExpr::AO__atomic_fetch_max:
1341 case AtomicExpr::AO__c11_atomic_fetch_max:
1342 case AtomicExpr::AO__hip_atomic_fetch_max:
1343 case AtomicExpr::AO__opencl_atomic_fetch_max:
1344 case AtomicExpr::AO__scoped_atomic_fetch_max:
1345 case AtomicExpr::AO__scoped_atomic_max_fetch:
1346 case AtomicExpr::AO__scoped_atomic_fetch_uinc:
1347 case AtomicExpr::AO__scoped_atomic_fetch_udec:
1348 case AtomicExpr::AO__atomic_test_and_set:
1349 case AtomicExpr::AO__atomic_clear:
1350 case AtomicExpr::AO__atomic_fetch_uinc:
1351 case AtomicExpr::AO__atomic_fetch_udec:
1352 llvm_unreachable("Integral atomic operations always become atomicrmw!");
1353 }
1354
1355 if (e->isOpenCL()) {
1357 cgf.cgm.errorNYI(loc, "emitLibCallForAtomicExpr: openCL");
1358 return RValue::get(nullptr);
1359 }
1360
1361 // By default, assume we return a value of the atomic type.
1362 if (!hasRetTy) {
1363 // Value is returned through parameter before the order.
1364 retTy = cgf.getContext().VoidTy;
1365 args.add(RValue::get(castToGenericAddrSpace(dest.emitRawPointer(), retTy)),
1366 cgf.getContext().VoidPtrTy);
1367 }
1368
1369 // Order is always the last parameter.
1370 args.add(RValue::get(cgf.emitScalarExpr(e->getOrder())),
1371 cgf.getContext().IntTy);
1372 if (e->isOpenCL()) {
1374 cgf.cgm.errorNYI(loc, "emitLibCallForAtomicExpr: openCL");
1375 return RValue::get(nullptr);
1376 }
1377
1378 RValue res = emitAtomicLibCall(cgf, calleeName, retTy, args);
1379
1380 // The value is returned directly from the libcall.
1381 if (e->isCmpXChg())
1382 return res;
1383
1384 if (resultTy->isVoidType())
1385 return RValue::get(nullptr);
1386
1387 return cgf.convertTempToRValue(
1388 dest.withElementType(cgf.getBuilder(), cgf.convertTypeForMem(resultTy)),
1389 resultTy, e->getExprLoc());
1390}
1391
1393 QualType atomicTy = e->getPtr()->getType()->getPointeeType();
1394 QualType memTy = atomicTy;
1395 if (const auto *ty = atomicTy->getAs<AtomicType>())
1396 memTy = ty->getValueType();
1397
1398 Expr *isWeakExpr = nullptr;
1399 Expr *orderFailExpr = nullptr;
1400
1401 Address val1 = Address::invalid();
1402 Address val2 = Address::invalid();
1403 Address dest = Address::invalid();
1405
1407 if (e->getOp() == AtomicExpr::AO__c11_atomic_init) {
1408 LValue lvalue = makeAddrLValue(ptr, atomicTy);
1409 emitAtomicInit(e->getVal1(), lvalue);
1410 return RValue::get(nullptr);
1411 }
1412
1413 TypeInfoChars typeInfo = getContext().getTypeInfoInChars(atomicTy);
1414 uint64_t size = typeInfo.Width.getQuantity();
1415
1416 // Emit the sync scope operand, and try to evaluate it as a constant.
1417 mlir::Value scope =
1418 e->getScopeModel() ? emitScalarExpr(e->getScope()) : nullptr;
1419 std::optional<Expr::EvalResult> scopeConst;
1420 if (Expr::EvalResult eval;
1421 e->getScopeModel() && e->getScope()->EvaluateAsInt(eval, getContext()))
1422 scopeConst.emplace(std::move(eval));
1423
1424 switch (e->getOp()) {
1425 default:
1426 cgm.errorNYI(e->getSourceRange(), "atomic op NYI");
1427 return RValue::get(nullptr);
1428
1429 case AtomicExpr::AO__c11_atomic_init:
1430 llvm_unreachable("already handled above with emitAtomicInit");
1431
1432 case AtomicExpr::AO__atomic_load_n:
1433 case AtomicExpr::AO__scoped_atomic_load_n:
1434 case AtomicExpr::AO__c11_atomic_load:
1435 case AtomicExpr::AO__atomic_test_and_set:
1436 case AtomicExpr::AO__atomic_clear:
1437 break;
1438
1439 case AtomicExpr::AO__atomic_load:
1440 case AtomicExpr::AO__scoped_atomic_load:
1441 dest = emitPointerWithAlignment(e->getVal1());
1442 break;
1443
1444 case AtomicExpr::AO__atomic_store:
1445 case AtomicExpr::AO__scoped_atomic_store:
1446 val1 = emitPointerWithAlignment(e->getVal1());
1447 break;
1448
1449 case AtomicExpr::AO__atomic_exchange:
1450 case AtomicExpr::AO__scoped_atomic_exchange:
1451 val1 = emitPointerWithAlignment(e->getVal1());
1452 dest = emitPointerWithAlignment(e->getVal2());
1453 break;
1454
1455 case AtomicExpr::AO__atomic_compare_exchange:
1456 case AtomicExpr::AO__atomic_compare_exchange_n:
1457 case AtomicExpr::AO__c11_atomic_compare_exchange_weak:
1458 case AtomicExpr::AO__c11_atomic_compare_exchange_strong:
1459 case AtomicExpr::AO__scoped_atomic_compare_exchange:
1460 case AtomicExpr::AO__scoped_atomic_compare_exchange_n:
1461 val1 = emitPointerWithAlignment(e->getVal1());
1462 if (e->getOp() == AtomicExpr::AO__atomic_compare_exchange ||
1463 e->getOp() == AtomicExpr::AO__scoped_atomic_compare_exchange)
1464 val2 = emitPointerWithAlignment(e->getVal2());
1465 else
1466 val2 = emitValToTemp(*this, e->getVal2());
1467 orderFailExpr = e->getOrderFail();
1468 if (e->getOp() == AtomicExpr::AO__atomic_compare_exchange_n ||
1469 e->getOp() == AtomicExpr::AO__atomic_compare_exchange ||
1470 e->getOp() == AtomicExpr::AO__scoped_atomic_compare_exchange_n ||
1471 e->getOp() == AtomicExpr::AO__scoped_atomic_compare_exchange)
1472 isWeakExpr = e->getWeak();
1473 break;
1474
1475 case AtomicExpr::AO__c11_atomic_fetch_add:
1476 case AtomicExpr::AO__c11_atomic_fetch_sub:
1477 if (memTy->isPointerType()) {
1478 // For pointer arithmetic, we're required to do a bit of math:
1479 // adding 1 to an int* is not the same as adding 1 to a uintptr_t.
1480 // ... but only for the C11 builtins. The GNU builtins expect the
1481 // user to multiply by sizeof(T).
1482 QualType val1Ty = e->getVal1()->getType();
1483 mlir::Location loc = getLoc(e->getSourceRange());
1484 mlir::Value val1Scalar = emitScalarExpr(e->getVal1());
1485 CharUnits pointeeIncAmt =
1486 getContext().getTypeSizeInChars(memTy->getPointeeType());
1487 mlir::Value scale = builder.getConstInt(loc, val1Scalar.getType(),
1488 pointeeIncAmt.getQuantity());
1489 val1Scalar = builder.createMul(loc, val1Scalar, scale);
1490 val1 = createMemTemp(val1Ty, loc, ".atomictmp");
1491 emitStoreOfScalar(val1Scalar, makeAddrLValue(val1, val1Ty),
1492 /*isInit=*/true);
1493 }
1494 [[fallthrough]];
1495 case AtomicExpr::AO__atomic_fetch_add:
1496 case AtomicExpr::AO__atomic_fetch_sub:
1497 case AtomicExpr::AO__atomic_add_fetch:
1498 case AtomicExpr::AO__atomic_sub_fetch:
1499 if (memTy->isPointerType()) {
1500 // Fetch-and-update atomic operation on pointers should treat the pointer
1501 // value as uintptr_t values
1502 if (!val1.isValid())
1503 val1 = emitValToTemp(*this, e->getVal1());
1504 ptr = ptr.withElementType(builder, val1.getElementType());
1505 break;
1506 }
1507 [[fallthrough]];
1508 case AtomicExpr::AO__atomic_fetch_max:
1509 case AtomicExpr::AO__atomic_fetch_min:
1510 case AtomicExpr::AO__atomic_max_fetch:
1511 case AtomicExpr::AO__atomic_min_fetch:
1512 case AtomicExpr::AO__c11_atomic_fetch_max:
1513 case AtomicExpr::AO__c11_atomic_fetch_min:
1514 case AtomicExpr::AO__scoped_atomic_fetch_add:
1515 case AtomicExpr::AO__scoped_atomic_fetch_max:
1516 case AtomicExpr::AO__scoped_atomic_fetch_min:
1517 case AtomicExpr::AO__scoped_atomic_fetch_sub:
1518 case AtomicExpr::AO__scoped_atomic_fetch_fminimum:
1519 case AtomicExpr::AO__scoped_atomic_fetch_fmaximum:
1520 case AtomicExpr::AO__scoped_atomic_fetch_fminimum_num:
1521 case AtomicExpr::AO__scoped_atomic_fetch_fmaximum_num:
1522 case AtomicExpr::AO__scoped_atomic_add_fetch:
1523 case AtomicExpr::AO__scoped_atomic_max_fetch:
1524 case AtomicExpr::AO__scoped_atomic_min_fetch:
1525 case AtomicExpr::AO__scoped_atomic_sub_fetch:
1526 [[fallthrough]];
1527
1528 case AtomicExpr::AO__atomic_fetch_and:
1529 case AtomicExpr::AO__atomic_fetch_nand:
1530 case AtomicExpr::AO__atomic_fetch_or:
1531 case AtomicExpr::AO__atomic_fetch_xor:
1532 case AtomicExpr::AO__atomic_and_fetch:
1533 case AtomicExpr::AO__atomic_nand_fetch:
1534 case AtomicExpr::AO__atomic_or_fetch:
1535 case AtomicExpr::AO__atomic_xor_fetch:
1536 case AtomicExpr::AO__atomic_exchange_n:
1537 case AtomicExpr::AO__atomic_store_n:
1538 case AtomicExpr::AO__c11_atomic_fetch_and:
1539 case AtomicExpr::AO__c11_atomic_fetch_nand:
1540 case AtomicExpr::AO__c11_atomic_fetch_or:
1541 case AtomicExpr::AO__c11_atomic_fetch_xor:
1542 case AtomicExpr::AO__c11_atomic_exchange:
1543 case AtomicExpr::AO__c11_atomic_store:
1544 case AtomicExpr::AO__scoped_atomic_fetch_and:
1545 case AtomicExpr::AO__scoped_atomic_fetch_nand:
1546 case AtomicExpr::AO__scoped_atomic_fetch_or:
1547 case AtomicExpr::AO__scoped_atomic_fetch_xor:
1548 case AtomicExpr::AO__scoped_atomic_and_fetch:
1549 case AtomicExpr::AO__scoped_atomic_nand_fetch:
1550 case AtomicExpr::AO__scoped_atomic_or_fetch:
1551 case AtomicExpr::AO__scoped_atomic_xor_fetch:
1552 case AtomicExpr::AO__scoped_atomic_store_n:
1553 case AtomicExpr::AO__scoped_atomic_exchange_n:
1554 case AtomicExpr::AO__atomic_fetch_uinc:
1555 case AtomicExpr::AO__atomic_fetch_udec:
1556 case AtomicExpr::AO__scoped_atomic_fetch_uinc:
1557 case AtomicExpr::AO__scoped_atomic_fetch_udec:
1558 case AtomicExpr::AO__atomic_fetch_fminimum:
1559 case AtomicExpr::AO__atomic_fetch_fmaximum:
1560 case AtomicExpr::AO__atomic_fetch_fminimum_num:
1561 case AtomicExpr::AO__atomic_fetch_fmaximum_num:
1562 val1 = emitValToTemp(*this, e->getVal1());
1563 break;
1564 }
1565
1566 QualType resultTy = e->getType().getUnqualifiedType();
1567
1568 bool shouldCastToIntPtrTy =
1570
1571 // The inlined atomics only function on iN types, where N is a power of 2. We
1572 // need to make sure (via temporaries if necessary) that all incoming values
1573 // are compatible.
1574 mlir::Location loc = getLoc(e->getSourceRange());
1575 LValue atomicValue = makeAddrLValue(ptr, atomicTy);
1576 AtomicInfo atomics(*this, atomicValue, loc);
1577
1578 if (shouldCastToIntPtrTy) {
1579 ptr = atomics.castToAtomicIntPointer(ptr);
1580 if (val1.isValid())
1581 val1 = atomics.convertToAtomicIntPointer(val1, loc);
1582 if (val2.isValid())
1583 val2 = atomics.convertToAtomicIntPointer(val2, loc);
1584 }
1585 if (dest.isValid()) {
1586 if (shouldCastToIntPtrTy)
1587 dest = atomics.castToAtomicIntPointer(dest);
1588 } else if (e->isCmpXChg()) {
1589 dest = createMemTemp(resultTy, loc, "cmpxchg.bool");
1590 } else if (e->getOp() == AtomicExpr::AO__atomic_test_and_set) {
1591 dest = createMemTemp(resultTy, loc, "test_and_set.bool");
1592 } else if (!resultTy->isVoidType()) {
1593 dest = atomics.createTempAlloca();
1594 if (shouldCastToIntPtrTy)
1595 dest = atomics.castToAtomicIntPointer(dest);
1596 }
1597
1598 bool powerOf2Size = (size & (size - 1)) == 0;
1599 bool useLibCall = !powerOf2Size || (size > 16);
1600
1601 // For atomics larger than 16 bytes, emit a libcall from the frontend. This
1602 // avoids the overhead of dealing with excessively-large value types in IR.
1603 // Non-power-of-2 values also lower to libcall here, as they are not currently
1604 // permitted in IR instructions (although that constraint could be relaxed in
1605 // the future). For other cases where a libcall is required on a given
1606 // platform, we let the backend handle it (this includes handling for all of
1607 // the size-optimized libcall variants, which are only valid up to 16 bytes.)
1608 //
1609 // See: https://llvm.org/docs/Atomics.html#libcalls-atomic
1610 if (useLibCall)
1611 return emitLibCallForAtomicExpr(*this, e, ptr, dest, val1, size, resultTy);
1612
1613 bool isStore = e->getOp() == AtomicExpr::AO__c11_atomic_store ||
1614 e->getOp() == AtomicExpr::AO__opencl_atomic_store ||
1615 e->getOp() == AtomicExpr::AO__hip_atomic_store ||
1616 e->getOp() == AtomicExpr::AO__atomic_store ||
1617 e->getOp() == AtomicExpr::AO__atomic_store_n ||
1618 e->getOp() == AtomicExpr::AO__scoped_atomic_store ||
1619 e->getOp() == AtomicExpr::AO__scoped_atomic_store_n ||
1620 e->getOp() == AtomicExpr::AO__atomic_clear;
1621 bool isLoad = e->getOp() == AtomicExpr::AO__c11_atomic_load ||
1622 e->getOp() == AtomicExpr::AO__opencl_atomic_load ||
1623 e->getOp() == AtomicExpr::AO__hip_atomic_load ||
1624 e->getOp() == AtomicExpr::AO__atomic_load ||
1625 e->getOp() == AtomicExpr::AO__atomic_load_n ||
1626 e->getOp() == AtomicExpr::AO__scoped_atomic_load ||
1627 e->getOp() == AtomicExpr::AO__scoped_atomic_load_n;
1628
1629 auto emitAtomicOpCallBackFn = [&](cir::MemOrder memOrder) {
1630 emitAtomicOp(*this, e, dest, ptr, val1, val2, isWeakExpr, orderFailExpr,
1631 size, memOrder, scopeConst, scope);
1632 };
1633 emitAtomicExprWithMemOrder(e->getOrder(), isStore, isLoad, /*isFence*/ false,
1634 emitAtomicOpCallBackFn);
1635
1636 if (resultTy->isVoidType())
1637 return RValue::get(nullptr);
1638
1639 return convertTempToRValue(
1640 dest.withElementType(builder, convertTypeForMem(resultTy)), resultTy,
1641 e->getExprLoc());
1642}
1643
1645 AggValueSlot slot) {
1646 if (lvalue.getType()->isAtomicType())
1647 return emitAtomicLoad(lvalue, loc, cir::MemOrder::SequentiallyConsistent,
1648 /*isVolatile=*/lvalue.isVolatileQualified(), slot);
1649 return emitAtomicLoad(lvalue, loc, cir::MemOrder::Acquire,
1650 /*isVolatile=*/true, slot);
1651}
1652
1654 cir::MemOrder order, bool isVolatile,
1655 AggValueSlot slot) {
1656 AtomicInfo info(*this, lvalue, getLoc(loc));
1657 return info.emitAtomicLoad(slot, loc, /*asValue=*/true, order, isVolatile);
1658}
1659
1660void CIRGenFunction::emitAtomicStore(RValue rvalue, LValue dest, bool isInit) {
1661 bool isVolatile = dest.isVolatileQualified();
1662 auto order = cir::MemOrder::SequentiallyConsistent;
1663 if (!dest.getType()->isAtomicType()) {
1665 }
1666 return emitAtomicStore(rvalue, dest, order, isVolatile, isInit);
1667}
1668
1669/// Emit a store to an l-value of atomic type.
1670///
1671/// Note that the r-value is expected to be an r-value of the atomic type; this
1672/// means that for aggregate r-values, it should include storage for any padding
1673/// that was necessary.
1675 cir::MemOrder order, bool isVolatile,
1676 bool isInit) {
1677 // If this is an aggregate r-value, it should agree in type except
1678 // maybe for address-space qualification.
1679 mlir::Location loc = dest.getPointer().getLoc();
1680 assert(!rvalue.isAggregate() ||
1682 dest.getAddress().getElementType());
1683
1684 AtomicInfo atomics(*this, dest, loc);
1685 LValue lvalue = atomics.getAtomicLValue();
1686
1687 if (lvalue.isSimple()) {
1688 // If this is an initialization, just put the value there normally.
1689 if (isInit) {
1690 atomics.emitCopyIntoMemory(rvalue);
1691 return;
1692 }
1693
1694 // Check whether we should use a library call.
1695 if (atomics.shouldUseLibCall()) {
1697 cgm.errorNYI(loc, "emitAtomicStore: atomic store with library call");
1698 return;
1699 }
1700
1701 // Okay, we're doing this natively.
1702 mlir::Value valueToStore = atomics.convertRValueToInt(rvalue, loc);
1703
1704 // Do the atomic store.
1705 Address addr = atomics.getAtomicAddress();
1706 if (mlir::Value value = atomics.getScalarRValValueOrNull(rvalue)) {
1707 if (shouldCastToInt(value.getType(), /*CmpXchg=*/false)) {
1708 addr = atomics.castToAtomicIntPointer(addr);
1709 valueToStore =
1710 builder.createIntCast(valueToStore, addr.getElementType());
1711 }
1712 }
1713 cir::StoreOp store = builder.createStore(loc, valueToStore, addr);
1714
1715 // Initializations don't need to be atomic.
1716 if (!isInit) {
1718 store.setMemOrder(order);
1719 }
1720
1721 // Other decoration.
1722 if (isVolatile)
1723 store.setIsVolatile(true);
1724
1726 return;
1727 }
1728
1729 cgm.errorNYI(loc, "emitAtomicStore: non-simple atomic lvalue");
1731}
1732
1734 AtomicInfo atomics(*this, dest, getLoc(init->getSourceRange()));
1735
1736 switch (atomics.getEvaluationKind()) {
1737 case cir::TEK_Scalar: {
1738 mlir::Value value = emitScalarExpr(init);
1739 atomics.emitCopyIntoMemory(RValue::get(value));
1740 return;
1741 }
1742
1743 case cir::TEK_Complex: {
1744 mlir::Value value = emitComplexExpr(init);
1745 atomics.emitCopyIntoMemory(RValue::get(value));
1746 return;
1747 }
1748
1749 case cir::TEK_Aggregate: {
1750 // Fix up the destination if the initializer isn't an expression
1751 // of atomic type.
1752 bool zeroed = false;
1753 if (!init->getType()->isAtomicType()) {
1754 zeroed = atomics.emitMemSetZeroIfNecessary();
1755 dest = atomics.projectValue();
1756 }
1757
1758 // Evaluate the expression directly into the destination.
1764
1765 emitAggExpr(init, slot);
1766 return;
1767 }
1768 }
1769
1770 llvm_unreachable("bad evaluation kind");
1771}
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:927
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:6940
static std::unique_ptr< AtomicScopeModel > getScopeModel(AtomicOp Op)
Get atomic scope model for the atomic op code.
Definition Expr.h:7089
Expr * getVal2() const
Definition Expr.h:6991
Expr * getOrder() const
Definition Expr.h:6974
Expr * getScope() const
Definition Expr.h:6977
bool isCmpXChg() const
Definition Expr.h:7024
AtomicOp getOp() const
Definition Expr.h:7003
bool isOpenCL() const
Definition Expr.h:7052
Expr * getVal1() const
Definition Expr.h:6981
Expr * getPtr() const
Definition Expr.h:6971
Expr * getWeak() const
Definition Expr.h:6997
Expr * getOrderFail() const
Definition Expr.h:6987
bool isVolatile() const
Definition Expr.h:7020
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.
RValue emitCall(const CIRGenFunctionInfo &funcInfo, const CIRGenCallee &callee, ReturnValueSlot returnValue, const CallArgList &args, cir::CIRCallOpInterface *callOp, mlir::Location loc)
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
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: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:283
QualType getType() const
Definition Expr.h:144
A (possibly-)qualified type.
Definition TypeBase.h:938
Qualifiers getQualifiers() const
Retrieve the set of qualifiers applied to this type.
Definition TypeBase.h:8529
QualType getUnqualifiedType() const
Retrieve the unqualified variant of the given type, removing as little sugar as possible.
Definition TypeBase.h:8583
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:868
bool isVoidType() const
Definition TypeBase.h:9092
bool isPointerType() const
Definition TypeBase.h:8726
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:8918
const T * getAs() const
Member-template getAs<specific type>'.
Definition TypeBase.h:9319
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 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:652
APValue Val
Val - This is the value the expression can be folded to.
Definition Expr.h:654