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